feat: support macOS pyvsag wheel builds#2411
Conversation
Build macOS pyvsag wheels for supported CPython 3.10-3.14 on both arm64 and x86_64 runners with a macOS 11 deployment target. Use bundled static dependencies for macOS wheels, disable OpenMP in macOS Python packaging, and set @loader_path rpath so installed wheels can load their packaged libvsag dylib on clean machines. Keep macOS x86_64 OpenBLAS on a conservative baseline target while preserving VSAG runtime SIMD dispatch. Keep filtered workflow-dispatch validation green by turning empty build matrices into no-op jobs and keeping publish as a non-release no-op. Use the existing PyPI publish action ref so skipped release-only publish steps do not break workflow-dispatch runs. Keep local macOS wheel builds on the same dependency policy and pass the configured parallel job count through to third-party builds. Document the macOS wheel support surface for PyPI users. Signed-off-by: jac0626 <jac0626@users.noreply.github.com> Assisted-by: Codex:GPT-5
Merge Protections🟢 All 3 merge protections satisfied — ready to merge. Show 3 satisfied protections🟢 Require kind label
🟢 Require version label
🟢 Require linked issue for feature/bug PRs
|
There was a problem hiding this comment.
Code Review
This pull request introduces macOS support for building and packaging pyvsag wheels, refactors OpenMP configuration to be conditionally enabled via ENABLE_OPENMP, and updates dependency handling (such as fmt and OpenBLAS) for better compatibility on macOS. Key feedback includes: wrapping vsag_src_common in $<BUILD_INTERFACE:...> for vsag_static to prevent CMake export errors; avoiding pip install --user failures in active virtual environments and preventing path corruption from uv python install output in local_build_wheel.sh; using IN_LIST instead of STREQUAL for checking CMAKE_OSX_ARCHITECTURES in openblas.cmake; and removing redundant raw compiler include flags (-I, -iquote) in fmt.cmake in favor of standard CMake target include directories.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds macOS wheel build support for pyvsag across Apple Silicon and Intel, while adjusting build/linking behavior (notably OpenMP and dependency bundling) and documenting the new support surface.
Changes:
- Add macOS wheel builds to CI matrix (CPython 3.10–3.14; arm64 + x86_64) and update local wheel build scripts.
- Introduce OpenMP toggling helpers and macOS-specific wheel build configuration (disable OpenMP, adjust rpath/loader behavior).
- Update docs to reflect macOS wheel availability and supported versions/targets.
Reviewed changes
Copilot reviewed 25 out of 26 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/build_release_wheel.yml |
Adds macOS build matrix/jobs and modifies publish gating logic. |
python/pyproject.toml |
Splits cibuildwheel config by platform, adds macOS before-build hook. |
python/setup.py |
Adds macOS-specific CMake configuration for wheel builds; controls parallelism/flags. |
scripts/python/local_build_wheel.sh |
Adds macOS-local wheel build path and Python resolution/venv management. |
scripts/python/prepare_python_build.sh |
Uses an isolated venv for setuptools_scm version generation. |
scripts/deps/install_deps_macos.sh |
Adds uv installation and prints its prefix. |
cmake/VSAGHelpers.cmake |
Adds vsag_enable_openmp_* helpers and gates OpenMP on ENABLE_OPENMP. |
cmake/DarwinDep.cmake |
Makes OpenMP configuration conditional and adds clearer disabled path. |
cmake/VSAGPythonBindings.cmake |
Applies OpenMP helper and sets @loader_path rpath for _pyvsag on macOS. |
extern/openblas/openblas.cmake |
Adds macOS-specific OpenBLAS build args (x86_64 baseline vs arm64 dynamic arch). |
extern/fmt/fmt.cmake |
Adjusts fmt header handling on macOS to prefer bundled includes. |
src/omp.h |
Adds an OpenMP shim header for builds without OpenMP available/enabled. |
docs/docs/**/guide/{installation,pyvsag}.md |
Documents macOS wheel availability and the supported macOS/Python range. |
(various CMakeLists.txt) |
Replaces raw -fopenmp flags with OpenMP helper functions and adds gating definitions. |
Comments suppressed due to low confidence (1)
python/pyproject.toml:1
before-allis currently defined at the global[tool.cibuildwheel]level, but it contains CentOS/yum-specific commands. With macOS wheel jobs added, cibuildwheel will attempt to run this on macOS too, which will fail. Move thisbefore-allblock under[tool.cibuildwheel.linux](or split per-platform) so macOS builds don't execute Linux-only setup.
[build-system]
b2d4e81 to
685e76b
Compare
c180d6d to
1c6f1b9
Compare
|
/label S-waiting-on-review |
1c6f1b9 to
ec16d23
Compare
Signed-off-by: jc543239 <jc543239@antgroup.com> Assisted-by: Codex:GPT-5
| join_versions() { | ||
| local IFS=", " | ||
| echo "$*" | ||
| } |
| if command -v brew >/dev/null 2>&1; then | ||
| echo " - Installing uv with Homebrew..." >&2 | ||
| brew install uv | ||
| command -v uv | ||
| return 0 | ||
| fi |
| echo " - Installing uv to manage missing local Python versions..." >&2 | ||
| python3 -m pip install --user --timeout 60 -q uv | ||
|
|
| uv_bin=$(ensure_uv) | ||
| echo " - Python ${py_version} not found locally; installing with uv..." >&2 | ||
| "${uv_bin}" python install "${py_version}" | ||
| "${uv_bin}" python find "${py_version}" | ||
| return 0 |
| if ${use_uvx}; then | ||
| python3 -m pip install -q uv 2>/dev/null || true | ||
| CIBW_BUILD="${cibw_build_pattern}" \ |
| echo "modern_matrix=$(matrix_or_skip "$modern_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT | ||
| echo "macos_matrix=$(matrix_or_skip "$macos_matrix" macos-15)" >> $GITHUB_OUTPUT | ||
| echo "legacy_matrix=$(matrix_or_skip "$legacy_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT | ||
| echo "py36_matrix=$(matrix_or_skip "$py36_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| else | ||
| modern_matrix="[]" | ||
| fi | ||
| echo "modern_matrix=$(matrix_or_skip "$modern_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| echo "modern_matrix=[]" >> $GITHUB_OUTPUT | ||
| macos_matrix="[]" | ||
| fi | ||
| echo "macos_matrix=$(matrix_or_skip "$macos_matrix" macos-15)" >> $GITHUB_OUTPUT |
| echo "legacy_matrix=[]" >> $GITHUB_OUTPUT | ||
| legacy_matrix="[]" | ||
| fi | ||
| echo "legacy_matrix=$(matrix_or_skip "$legacy_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| echo "py36_matrix=[]" >> $GITHUB_OUTPUT | ||
| py36_matrix="[]" | ||
| fi | ||
| echo "py36_matrix=$(matrix_or_skip "$py36_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
14735ee to
640b2dd
Compare
| join_versions() { | ||
| local IFS=", " | ||
| echo "$*" | ||
| } |
| if command -v brew >/dev/null 2>&1; then | ||
| echo " - Installing uv with Homebrew..." >&2 | ||
| brew install uv | ||
| command -v uv | ||
| return 0 | ||
| fi |
| echo " - Installing uv to manage missing local Python versions..." >&2 | ||
| python3 -m pip install --user --timeout 60 -q uv | ||
|
|
| uv_bin=$(ensure_uv) | ||
| echo " - Python ${py_version} not found locally; installing with uv..." >&2 | ||
| "${uv_bin}" python install "${py_version}" | ||
| "${uv_bin}" python find "${py_version}" | ||
| return 0 |
| if ${HAVE_DOCKER}; then | ||
| echo "🛠️ Starting cibuildwheel..." | ||
| if ${use_uvx}; then | ||
| python3 -m pip install -q uv 2>/dev/null || true |
| else | ||
| modern_matrix="[]" | ||
| fi | ||
| echo "modern_matrix=$(matrix_or_skip "$modern_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| echo "modern_matrix=[]" >> $GITHUB_OUTPUT | ||
| macos_matrix="[]" | ||
| fi | ||
| echo "macos_matrix=$(matrix_or_skip "$macos_matrix" macos-15)" >> $GITHUB_OUTPUT |
| echo "legacy_matrix=[]" >> $GITHUB_OUTPUT | ||
| legacy_matrix="[]" | ||
| fi | ||
| echo "legacy_matrix=$(matrix_or_skip "$legacy_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| echo "py36_matrix=[]" >> $GITHUB_OUTPUT | ||
| py36_matrix="[]" | ||
| fi | ||
| echo "py36_matrix=$(matrix_or_skip "$py36_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| message (FATAL_ERROR | ||
| "OpenMP not found on macOS. Install dependencies via " | ||
| "scripts/deps/install_deps.sh.") |
640b2dd to
14735ee
Compare
| join_versions() { | ||
| local IFS=", " | ||
| echo "$*" | ||
| } |
| if command -v brew >/dev/null 2>&1; then | ||
| echo " - Installing uv with Homebrew..." >&2 | ||
| brew install uv | ||
| command -v uv | ||
| return 0 | ||
| fi | ||
| fi | ||
|
|
||
| # --- Auto-detect Architecture --- | ||
| ARCH=$(uname -m) | ||
| if [[ "$ARCH" == "arm64" ]]; then | ||
| ARCH="aarch64" | ||
| fi | ||
| echo "✅ Detected local architecture: $ARCH" | ||
| echo " - Installing uv to manage missing local Python versions..." >&2 | ||
| python3 -m pip install --user --timeout 60 -q uv | ||
|
|
| echo " - Python ${py_version} not found locally; installing with uv..." >&2 | ||
| "${uv_bin}" python install "${py_version}" | ||
| "${uv_bin}" python find "${py_version}" |
| if ${HAVE_DOCKER}; then | ||
| echo "🛠️ Starting cibuildwheel..." | ||
| if ${use_uvx}; then | ||
| python3 -m pip install -q uv 2>/dev/null || true |
| echo "modern_matrix=$(matrix_or_skip "$modern_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT | ||
| echo "macos_matrix=$(matrix_or_skip "$macos_matrix" macos-15)" >> $GITHUB_OUTPUT | ||
| echo "legacy_matrix=$(matrix_or_skip "$legacy_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT | ||
| echo "py36_matrix=$(matrix_or_skip "$py36_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| echo "legacy_matrix=[]" >> $GITHUB_OUTPUT | ||
| legacy_matrix="[]" | ||
| fi | ||
| echo "legacy_matrix=$(matrix_or_skip "$legacy_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| echo "py36_matrix=[]" >> $GITHUB_OUTPUT | ||
| py36_matrix="[]" | ||
| fi | ||
| echo "py36_matrix=$(matrix_or_skip "$py36_matrix" ubuntu-latest)" >> $GITHUB_OUTPUT |
| include_directories (BEFORE "${fmt_SOURCE_DIR}/include") | ||
| add_subdirectory (${fmt_SOURCE_DIR} ${fmt_BINARY_DIR} EXCLUDE_FROM_ALL) |
| jobs = os.environ.get("CMAKE_BUILD_PARALLEL_LEVEL", str(os.cpu_count() or 4)) | ||
| release_flags = "-O3 -DNDEBUG" | ||
| if sys.platform != "darwin": | ||
| release_flags += " -s" | ||
|
|
| if sys.platform == "darwin": | ||
| cmake_args.extend( | ||
| [ | ||
| "-DCMAKE_IGNORE_PREFIX_PATH=/usr/local;/opt/homebrew", | ||
| "-DENABLE_LIBAIO=OFF", | ||
| "-DENABLE_LIBCXX=ON", | ||
| "-DENABLE_OPENMP=OFF", | ||
| "-DENABLE_WERROR=OFF", | ||
| "-DCMAKE_C_COMPILER=clang", | ||
| "-DCMAKE_CXX_COMPILER=clang++", | ||
| ] | ||
| ) |
Change Type
Linked Issue
What Changed
pyvsagwheel builds for supported CPython 3.10-3.14 on Apple Silicon (arm64) and Intel (x86_64) runners.@loader_pathloading for packagedlibvsag.dylib.libomp.dylibat import time.libvsag.dylibinstead of three copied version aliases.Test Evidence
make fmtmake lintmake testmake cov, run tests, and collect coverageTest details:
Compatibility Impact
pip install pyvsagcan install prebuilt macOS wheels for CPython 3.10-3.14 on arm64 and x86_64. macOS Python wheels do not enable OpenMP to avoid externallibomp.dylibruntime dependencies. Linux wheel behavior is unchanged.Performance and Concurrency Impact
Documentation Impact
README.mdDEVELOPMENT.mdCONTRIBUTING.mddocs/docs/en/src/guide/{installation,pyvsag}.md,docs/docs/zh/src/guide/{installation,pyvsag}.mdRisk and Rollback
Checklist
kind/bugandkind/feature; see "Linked Issue" above)[skip ci]prefix)