Skip to content

Commit e579b8f

Browse files
alibeklfcfacebook-github-bot
authored andcommitted
Add pip install support via scikit-build-core + cibuildwheel (#4862)
Summary: Add official pip wheel packaging for `faiss-cpu`, enabling `pip install faiss-cpu` from PyPI. Builds wheels across Linux x86_64/aarch64, macOS arm64/x86_64, and Windows x86_64 for Python 3.10-3.13. **New files:** - `pyproject.toml`: scikit-build-core build backend config with cibuildwheel settings. Uses Dynamic Dispatch (`FAISS_OPT_LEVEL=dd`) for runtime SIMD selection, OpenBLAS for BLAS, and disables GPU/MKL/SVS. - `.github/workflows/build-pip.yml`: CI workflow using cibuildwheel to build wheels on 5 platform runners. Publishes to PyPI via OIDC trusted publishers on tag push. - `tests/test_wheel_smoke.py`: 11-check smoke test suite validating import, OpenMP, BLAS (FlatL2/FlatIP), index factory (IVF+PQ), HNSW, serialization roundtrip, GC safety, contrib imports, and SIMD level detection. **Modified files:** - `python/CMakeLists.txt`: Added `install()` targets so scikit-build-core can package the SWIG extension module, Python source files, and contrib subpackage into wheels. - `.github/workflows/build.yml`: Wired `build-pip.yml` into the root CI trigger so pip builds run alongside conda builds. Differential Revision: D95258115
1 parent 1cb3d46 commit e579b8f

5 files changed

Lines changed: 444 additions & 1 deletion

File tree

.github/workflows/build-pip.yml

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
name: Build pip wheels
7+
8+
on:
9+
workflow_call:
10+
workflow_dispatch:
11+
pull_request:
12+
branches:
13+
- main
14+
paths:
15+
- 'pyproject.toml'
16+
- 'faiss/python/**'
17+
- '.github/workflows/build-pip.yml'
18+
19+
jobs:
20+
build-wheels:
21+
name: Build wheels on ${{ matrix.os }}
22+
runs-on: ${{ matrix.os }}
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
os: [ubuntu-latest, macos-14, windows-2022, 2-core-ubuntu-arm]
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@v4
30+
with:
31+
fetch-depth: 0
32+
fetch-tags: true
33+
34+
- name: Build wheels
35+
uses: pypa/cibuildwheel@v2.22
36+
env:
37+
CIBW_BUILD_VERBOSITY: 1
38+
39+
- name: Upload wheels
40+
uses: actions/upload-artifact@v4
41+
with:
42+
name: wheels-${{ matrix.os }}
43+
path: wheelhouse/*.whl
44+
45+
build-sdist:
46+
name: Build source distribution
47+
runs-on: ubuntu-latest
48+
steps:
49+
- name: Checkout
50+
uses: actions/checkout@v4
51+
with:
52+
fetch-depth: 0
53+
fetch-tags: true
54+
55+
- name: Build sdist
56+
run: pipx run build --sdist
57+
58+
- name: Upload sdist
59+
uses: actions/upload-artifact@v4
60+
with:
61+
name: sdist
62+
path: dist/*.tar.gz
63+
64+
publish:
65+
name: Publish to PyPI
66+
needs: [build-wheels, build-sdist]
67+
runs-on: ubuntu-latest
68+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
69+
environment:
70+
name: pypi
71+
url: https://pypi.org/p/faiss-cpu
72+
permissions:
73+
id-token: write
74+
steps:
75+
- name: Download all artifacts
76+
uses: actions/download-artifact@v4
77+
with:
78+
path: dist
79+
merge-multiple: true
80+
81+
- name: Publish to PyPI
82+
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ jobs:
1515
secrets:
1616
ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }}
1717
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
18+
build-pip:
19+
uses: ./.github/workflows/build-pip.yml

faiss/python/CMakeLists.txt

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ find_package(Python REQUIRED
299299
COMPONENTS Development.Module NumPy
300300
)
301301

302-
add_library(faiss_python_callbacks EXCLUDE_FROM_ALL
302+
add_library(faiss_python_callbacks STATIC EXCLUDE_FROM_ALL
303303
python_callbacks.cpp
304304
)
305305
set_property(TARGET faiss_python_callbacks
@@ -332,3 +332,66 @@ configure_file(array_conversions.py array_conversions.py COPYONLY)
332332

333333
# file(GLOB files "${PROJECT_SOURCE_DIR}/../../contrib/*.py")
334334
file(COPY ${PROJECT_SOURCE_DIR}/../../contrib DESTINATION .)
335+
336+
# =============================================================================
337+
# Install targets for scikit-build-core / pip wheel packaging.
338+
# These install the Python package into the wheel via cmake --install.
339+
# =============================================================================
340+
341+
# Set RPATH so the SWIG module finds libfaiss in the same directory.
342+
# auditwheel/delocate follow RPATH to locate libraries for bundling.
343+
if(APPLE)
344+
set_target_properties(swigfaiss PROPERTIES INSTALL_RPATH "@loader_path")
345+
else()
346+
set_target_properties(swigfaiss PROPERTIES INSTALL_RPATH "$ORIGIN")
347+
endif()
348+
349+
# Install the SWIG extension module (.so/.pyd)
350+
install(TARGETS swigfaiss
351+
LIBRARY DESTINATION faiss
352+
RUNTIME DESTINATION faiss
353+
)
354+
355+
# Install the core faiss shared library into the Python package directory
356+
# so that auditwheel/delocate can find and bundle it with the SWIG module.
357+
install(TARGETS faiss
358+
LIBRARY DESTINATION faiss
359+
RUNTIME DESTINATION faiss
360+
)
361+
362+
# Install SWIG-generated Python wrapper
363+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/swigfaiss.py"
364+
DESTINATION faiss
365+
)
366+
367+
# Install hand-written Python source files
368+
install(FILES
369+
"${CMAKE_CURRENT_SOURCE_DIR}/__init__.py"
370+
"${CMAKE_CURRENT_SOURCE_DIR}/loader.py"
371+
"${CMAKE_CURRENT_SOURCE_DIR}/class_wrappers.py"
372+
"${CMAKE_CURRENT_SOURCE_DIR}/extra_wrappers.py"
373+
"${CMAKE_CURRENT_SOURCE_DIR}/gpu_wrappers.py"
374+
"${CMAKE_CURRENT_SOURCE_DIR}/array_conversions.py"
375+
DESTINATION faiss
376+
)
377+
378+
# Install type stubs if present
379+
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/__init__.pyi")
380+
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/__init__.pyi" DESTINATION faiss)
381+
endif()
382+
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/py.typed")
383+
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/py.typed" DESTINATION faiss)
384+
endif()
385+
386+
# Install contrib subpackage (excluding Meta-internal *_fb.py files)
387+
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../contrib/"
388+
DESTINATION faiss/contrib
389+
FILES_MATCHING PATTERN "*.py"
390+
PATTERN "*_fb.py" EXCLUDE
391+
)
392+
393+
# Install contrib/torch subpackage data files
394+
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../contrib/torch/"
395+
DESTINATION faiss/contrib/torch
396+
FILES_MATCHING PATTERN "*.md"
397+
)

pyproject.toml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
[build-system]
7+
requires = ["scikit-build-core>=0.10", "numpy>=2.0"]
8+
build-backend = "scikit_build_core.build"
9+
10+
[project]
11+
name = "faiss-cpu"
12+
version = "1.14.0"
13+
description = "A library for efficient similarity search and clustering of dense vectors."
14+
readme = "README.md"
15+
license = "MIT"
16+
requires-python = ">=3.10"
17+
authors = [
18+
{name = "Meta AI Research"},
19+
]
20+
keywords = ["search", "nearest-neighbors", "clustering", "vectors", "similarity"]
21+
classifiers = [
22+
"Development Status :: 5 - Production/Stable",
23+
"Intended Audience :: Developers",
24+
"Intended Audience :: Science/Research",
25+
"Operating System :: MacOS :: MacOS X",
26+
"Operating System :: Microsoft :: Windows",
27+
"Operating System :: POSIX :: Linux",
28+
"Programming Language :: C++",
29+
"Programming Language :: Python :: 3",
30+
"Programming Language :: Python :: 3.10",
31+
"Programming Language :: Python :: 3.11",
32+
"Programming Language :: Python :: 3.12",
33+
"Programming Language :: Python :: 3.13",
34+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
35+
]
36+
dependencies = ["numpy>=1.25", "packaging"]
37+
38+
[project.urls]
39+
Homepage = "https://github.com/facebookresearch/faiss"
40+
Documentation = "https://github.com/facebookresearch/faiss/wiki"
41+
Repository = "https://github.com/facebookresearch/faiss"
42+
Issues = "https://github.com/facebookresearch/faiss/issues"
43+
44+
[tool.scikit-build]
45+
cmake.build-type = "Release"
46+
47+
# Exclude C++ headers, cmake configs, and pkgconfig from the wheel.
48+
# Only the Python package (faiss/) and bundled shared libs remain.
49+
wheel.exclude = ["include/**", "share/**", "lib/cmake/**", "lib/pkgconfig/**"]
50+
51+
[tool.scikit-build.cmake.define]
52+
FAISS_OPT_LEVEL = "dd"
53+
FAISS_ENABLE_GPU = "OFF"
54+
FAISS_ENABLE_PYTHON = "ON"
55+
FAISS_ENABLE_MKL = "OFF"
56+
FAISS_ENABLE_C_API = "OFF"
57+
FAISS_ENABLE_EXTRAS = "OFF"
58+
FAISS_ENABLE_SVS = "OFF"
59+
BUILD_TESTING = "OFF"
60+
BUILD_SHARED_LIBS = "ON"
61+
62+
[tool.cibuildwheel]
63+
build = "cp310-* cp311-* cp312-* cp313-*"
64+
skip = "*-win32 *-manylinux_i686 *-musllinux_*"
65+
test-requires = ["numpy>=2.0,<3.0", "pytest"]
66+
test-command = "python -m pytest {project}/tests/test_wheel_smoke.py -v"
67+
68+
[tool.cibuildwheel.linux]
69+
# EPEL needed for openblas-devel on aarch64 (not in CentOS 7 base repos)
70+
before-all = "yum install -y epel-release && yum install -y openblas-devel swig"
71+
72+
[tool.cibuildwheel.macos]
73+
before-all = "brew install libomp openblas swig"
74+
75+
[tool.cibuildwheel.macos.environment]
76+
# Help CMake find brew-installed OpenBLAS and libomp on macOS.
77+
# FindOpenMP cannot auto-detect AppleClang + brew libomp, so we pass
78+
# the flags and library path explicitly.
79+
CMAKE_PREFIX_PATH = "$(brew --prefix openblas);$(brew --prefix libomp)"
80+
CMAKE_ARGS = "-DOpenMP_CXX_FLAGS=-Xpreprocessor;-fopenmp;-I$(brew --prefix libomp)/include -DOpenMP_CXX_LIB_NAMES=omp -DOpenMP_omp_LIBRARY=$(brew --prefix libomp)/lib/libomp.dylib"
81+
# Brew libomp targets macOS 14.0+; the wheel must declare the same floor
82+
# so that delocate does not reject the library.
83+
MACOSX_DEPLOYMENT_TARGET = "14.0"
84+
85+
[tool.cibuildwheel.windows]
86+
# Install SWIG and MKL (consistent with conda recipe).
87+
# nuget provides MKL with a predictable layout. We need both the devel
88+
# package (import libs + headers for building) and the runtime package
89+
# (DLLs for delvewheel to bundle into the wheel).
90+
before-all = "choco install -y swig && nuget install intelmkl.devel.win-x64 -Version 2024.2.2 -OutputDirectory C:\\mkl_pkg && nuget install intelmkl.redist.win-x64 -Version 2024.2.2 -OutputDirectory C:\\mkl_pkg"
91+
92+
[tool.cibuildwheel.windows.environment]
93+
# Dynamic Dispatch is not yet supported on Windows (the DD cmake block is
94+
# gated behind if(NOT WIN32)). Fall back to generic baseline, matching the
95+
# conda recipe which also builds "vanilla (no avx)" on Windows.
96+
# Use MKL for BLAS (consistent with conda). MKL is installed via nuget
97+
# in before-all. FindMKL.cmake on Windows searches the LIB env var, and
98+
# delvewheel searches PATH for runtime DLLs to bundle.
99+
MKLROOT = "C:/mkl_pkg/intelmkl.devel.win-x64.2024.2.2"
100+
LIB = "C:\\mkl_pkg\\intelmkl.devel.win-x64.2024.2.2\\lib\\native\\win-x64"
101+
PATH = "C:\\mkl_pkg\\intelmkl.redist.win-x64.2024.2.2\\runtimes\\win-x64\\native;$PATH"
102+
CMAKE_ARGS = "-DFAISS_OPT_LEVEL=generic -DFAISS_ENABLE_MKL=ON -DBLA_VENDOR=Intel10_64_dyn"

0 commit comments

Comments
 (0)