Skip to content

Add ARM wheels build in CI #15

Add ARM wheels build in CI

Add ARM wheels build in CI #15

name: Build ARM Wheels for Paddle
on:
push:
branches: [paddle]
tags: ["v*"]
pull_request:
merge_group:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }}
cancel-in-progress: true
permissions:
id-token: write
contents: write
defaults:
run:
shell: bash -l -eo pipefail {0}
env:
PADDLECODEC_TEST_VIDEO_URL: https://paddlenlp.bj.bcebos.com/datasets/paddlemix/demo_video/example_video.mp4
PADDLECODEC_TEST_VIDEO_CACHE_KEY: paddlecodec-test-video-v1-example-video
PADDLECODEC_TEST_VIDEO_PATH: .github/test-assets/example_video.mp4
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
build-matrix: ${{ steps.set-matrix.outputs.build-matrix }}
test-matrix: ${{ steps.set-matrix.outputs.test-matrix }}
steps:
- name: Generate ARM build and test matrix
id: set-matrix
run: |
python - <<'PY'
import itertools
import json
import os
python_versions = ["3.9", "3.10", "3.11", "3.12", "3.13"]
# FFmpeg 8.0 is intentionally excluded for Paddle CPU builds to match
# the existing x86 workflow and avoid the current OpenVINO conflict.
ffmpeg_versions = ["4.4.2", "5.1.2", "6.1.1", "7.0.1"]
build_matrix = {"python-version": python_versions}
test_matrix = {
"include": [
{
"python-version": python_version,
"ffmpeg-version": ffmpeg_version,
}
for python_version, ffmpeg_version in itertools.product(
python_versions, ffmpeg_versions
)
]
}
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as f:
f.write(f"build-matrix={json.dumps(build_matrix)}\n")
f.write(f"test-matrix={json.dumps(test_matrix)}\n")
PY
prepare-test-video:
runs-on: ubuntu-latest
name: Prepare cached Paddle test video
steps:
- name: Restore cached test video
id: cache-test-video
uses: actions/cache@v4
with:
path: ${{ env.PADDLECODEC_TEST_VIDEO_PATH }}
key: ${{ env.PADDLECODEC_TEST_VIDEO_CACHE_KEY }}
- name: Download test video
if: steps.cache-test-video.outputs.cache-hit != 'true'
run: |
mkdir -p "$(dirname "${PADDLECODEC_TEST_VIDEO_PATH}")"
curl --fail --location --retry 5 --retry-all-errors \
--output "${PADDLECODEC_TEST_VIDEO_PATH}" \
"${PADDLECODEC_TEST_VIDEO_URL}"
- name: Upload cached test video artifact
uses: actions/upload-artifact@v5
with:
name: paddlecodec-test-video
path: ${{ env.PADDLECODEC_TEST_VIDEO_PATH }}
if-no-files-found: error
build:
needs: generate-matrix
name: Build and Upload ARM wheel
runs-on: ubuntu-24.04-arm
container:
image: pytorch/manylinux2_28_aarch64-builder:cpu-aarch64
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrix.outputs.build-matrix) }}
permissions:
id-token: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup conda environment
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
miniforge-version: latest
activate-environment: build
python-version: ${{ matrix.python-version }}
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build wheel setuptools
- name: Install PaddlePaddle nightly
run: |
pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/
- name: Run pre-build script
run: |
bash packaging/pre_build_script.sh
- name: Build wheel
run: |
# Match upstream's Linux workflow and build the ARM wheel against the
# pre-built non-GPL FFmpeg bundles published on S3.
export BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1
export I_CONFIRM_THIS_IS_NOT_A_LICENSE_VIOLATION=1
export TORCHCODEC_CMAKE_BUILD_DIR=$(pwd)/build_cmake
python -m build --wheel -vvv --no-isolation
- name: Repair wheel
run: |
pip install auditwheel
# 1. Extract internal libraries from the wheel to a temporary directory
# This allows auditwheel to find them when checking dependencies
mkdir -p temp_libs
unzip -j dist/*.whl "torchcodec/*.so" -d temp_libs || true
# 2. Prepare LD_LIBRARY_PATH
# FFmpeg libraries fetched from the build-time S3 bundles
FFMPEG_LIB_PATHS=$(find "$(pwd)/build_cmake/_deps" -type d -name "lib" -print | paste -sd: -)
# PaddlePaddle libraries
PADDLE_PATH=$(python -c "import paddle; print(paddle.__path__[0])")
PADDLE_LIB_PATHS="$PADDLE_PATH/base:$PADDLE_PATH/libs"
# Wheel internal libraries
INTERNAL_LIB_PATH=$(pwd)/temp_libs
export LD_LIBRARY_PATH=${FFMPEG_LIB_PATHS}:${PADDLE_LIB_PATHS}:${INTERNAL_LIB_PATH}:${LD_LIBRARY_PATH}
# 3. Repair wheel with auditwheel
# We exclude all external libraries because we want to rely on system libraries (like FFmpeg)
# or libraries provided by other packages (like PaddlePaddle).
# auditwheel 6.1.0+ supports wildcards in --exclude.
auditwheel repair dist/*.whl --plat manylinux_2_28_aarch64 -w wheelhouse/ --exclude "*"
# Cleanup
rm -rf temp_libs
rm dist/*.whl
mv wheelhouse/*.whl dist/
rmdir wheelhouse
- name: Upload wheel artifact
uses: actions/upload-artifact@v5
with:
name: paddlecodec-wheel-linux-arm64-py${{ matrix.python-version }}
path: dist/*.whl
- name: Run post-build script
run: |
bash packaging/post_build_script.sh
- name: List wheel contents
run: |
wheel_path=$(find dist -type f -name "*.whl")
echo "Wheel path: $wheel_path"
unzip -l $wheel_path
install-and-test:
needs: [generate-matrix, prepare-test-video, build]
name: Install and test ARM wheel
runs-on: ubuntu-24.04-arm
container:
image: pytorch/manylinux2_28_aarch64-builder:cpu-aarch64
env:
PADDLECODEC_TEST_VIDEO: .github/test-assets/example_video.mp4
strategy:
fail-fast: false
# Match the existing Paddle x86 flow while keeping the upstream-style staged
# build/test split: exercise every Python and FFmpeg combination post-build.
matrix: ${{ fromJson(needs.generate-matrix.outputs.test-matrix) }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Download wheel artifact
uses: actions/download-artifact@v4
with:
name: paddlecodec-wheel-linux-arm64-py${{ matrix.python-version }}
path: dist/
- name: Download cached test video artifact
uses: actions/download-artifact@v4
with:
name: paddlecodec-test-video
path: .github/test-assets/
- name: Install FFmpeg via conda
uses: conda-incubator/setup-miniconda@v3
with:
auto-update-conda: true
miniforge-version: latest
activate-environment: test
python-version: ${{ matrix.python-version }}
- name: Install FFmpeg from conda-forge
run: |
conda install "ffmpeg=${{ matrix.ffmpeg-version }}" -c conda-forge -y
ffmpeg -version
- name: Install PaddlePaddle nightly in conda env
run: |
pip install --pre paddlepaddle -i https://www.paddlepaddle.org.cn/packages/nightly/cpu/
- name: Install paddlecodec from wheel
run: |
wheel_path=$(find dist -type f -name "*.whl")
echo "Installing $wheel_path"
pip install $wheel_path -vvv
- name: Install test dependencies
run: |
pip install numpy pytest pillow
- name: Delete src folder
run: |
# Delete src/ to ensure we're testing the installed wheel, not source code
rm -rf src/
ls -la
- name: Run tests
run: |
pytest --override-ini="addopts=-v" -s test_paddle
publish-pypi-arm:
runs-on: ubuntu-latest
name: Publish ARM Wheels to PyPI
if: "startsWith(github.ref, 'refs/tags/')"
needs:
- install-and-test
permissions:
id-token: write
steps:
- name: Retrieve ARM release distributions
uses: actions/download-artifact@v6
with:
pattern: paddlecodec-wheel-linux-arm64-*
path: dist/
merge-multiple: true
- name: Publish ARM release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
publish-release-arm:
runs-on: ubuntu-latest
name: Publish ARM Wheels to GitHub
if: "startsWith(github.ref, 'refs/tags/')"
needs:
- install-and-test
permissions:
contents: write
steps:
- uses: actions/download-artifact@v6
with:
pattern: paddlecodec-wheel-linux-arm64-*
path: dist/
merge-multiple: true
- name: Publish ARM wheels to GitHub
uses: softprops/action-gh-release@v2
with:
draft: true
files: dist/*
tag_name: ${{ github.ref_name }}