Skip to content

Commit b24e3b5

Browse files
Fix cibuildwheel build configuration for manylinux_2_28 compatibility
- Add --plat manylinux_2_28_x86_64 flag to auditwheel repair command - Add CONDA_PREFIX environment variable for pixi build environment - Remove CMAKE_PREFIX_PATH from macOS cmake.args (shell variables don't expand) - Enable build isolation for cibuildwheel to fix Windows build - Remove --no-build-isolation flag which was causing scikit-build-core import error - The before-build script was not executing properly on Windows (completed in 0.01s) - With build isolation, pip automatically installs build-system.requires dependencies - Add CMAKE_PREFIX_PATH to Windows and macOS cibuildwheel environment - Points to {project}/.pixi/envs/default where dependencies are installed - Fixes 'Could not find Ceres' error on Windows - Fix PyPI Wheels workflow to use renamed pixi tasks - build_pypi_wheel -> wheel_build - build_pypi_repair -> wheel_repair - Rename wheel-related pixi tasks to consistent wheel_* naming convention - build_wheels -> wheel_build_cibw - build_pypi_wheel -> wheel_build - build_pypi_repair -> wheel_repair - check_pypi -> wheel_check - test_pypi_wheel -> wheel_test - publish_pypi -> wheel_publish - publish_pypi_test -> wheel_publish_testpypi This addresses GitHub issue #847 for manylinux_2_28 wheel compatibility to support older Ubuntu (20.04) and Google Colab environments.
1 parent 0f808ab commit b24e3b5

File tree

7 files changed

+1341
-125
lines changed

7 files changed

+1341
-125
lines changed

.github/workflows/cibuildwheel.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ jobs:
5252
run: |
5353
pixi install -e default
5454
echo "CMAKE_PREFIX_PATH=$PWD/.pixi/envs/default" >> $GITHUB_ENV
55+
echo "CONDA_PREFIX=$PWD/.pixi/envs/default" >> $GITHUB_ENV
5556
shell: bash
5657

5758
- name: Build wheels

.github/workflows/publish_to_pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ jobs:
139139

140140
- name: Build GPU wheel
141141
if: steps.should_run.outputs.run == 'true'
142-
run: pixi run -e ${{ matrix.pixi-environment }} build_pypi_wheel
142+
run: pixi run -e ${{ matrix.pixi-environment }} wheel_build
143143

144144
- name: Repair GPU wheel (Linux only)
145145
if: steps.should_run.outputs.run == 'true' && matrix.os == 'ubuntu-latest'
146-
run: pixi run -e ${{ matrix.pixi-environment }} build_pypi_repair
146+
run: pixi run -e ${{ matrix.pixi-environment }} wheel_repair
147147

148148
- name: Print ccache stats
149149
if: steps.should_run.outputs.run == 'true'

CMakeLists.txt

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,21 @@ find_package(urdfdom CONFIG REQUIRED)
128128

129129
if(MOMENTUM_BUILD_RENDERER)
130130
find_package(Kokkos CONFIG REQUIRED) # For mdspan headers (vendored in Kokkos)
131-
endif()
132-
133-
if(MOMENTUM_USE_SYSTEM_RERUN_CPP_SDK)
134-
find_package(rerun_sdk CONFIG REQUIRED)
135-
else()
136-
include(FetchContent)
137-
FetchContent_Declare(rerun_sdk
138-
URL https://github.com/rerun-io/rerun/releases/download/0.23.3/rerun_cpp_sdk.zip
139-
)
140-
FetchContent_MakeAvailable(rerun_sdk)
131+
if(MOMENTUM_USE_SYSTEM_RERUN_CPP_SDK)
132+
find_package(rerun_sdk CONFIG REQUIRED)
133+
else()
134+
include(FetchContent)
135+
FetchContent_Declare(
136+
rerun_sdk
137+
GIT_REPOSITORY https://github.com/rerun-io/rerun.git
138+
GIT_TAG 0.20.3
139+
)
140+
# Disable Rerun tests and examples to speed up build
141+
set(RERUN_BUILD_TESTING OFF CACHE BOOL "" FORCE)
142+
set(RERUN_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
143+
set(RERUN_BUILD_WEB_VIEWER OFF CACHE BOOL "" FORCE)
144+
FetchContent_MakeAvailable(rerun_sdk)
145+
endif()
141146
endif()
142147

143148
# Workaround for Arrow build failure in cibuildwheel

pixi.lock

Lines changed: 1209 additions & 58 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixi.toml

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ open_doc_py = { cmd = "open build/python_api_doc/index.html", depends-on = [
178178
# CPU environments (py312, py313) build CPU wheels
179179
# GPU environments (py312-cuda129, py313-cuda129) build GPU wheels
180180
# Usage:
181-
# CPU: pixi run -e py312 build_pypi_wheel
182-
# GPU: pixi run -e py312-cuda129 build_pypi_wheel
181+
# CPU: pixi run -e py312 wheel_build
182+
# GPU: pixi run -e py312-cuda129 wheel_build
183183

184184
# Generate pyproject-pypi-{cpu,gpu}.toml from template
185185
# This task is mainly for CI. For local use, run directly:
@@ -193,52 +193,50 @@ clean_dist = { cmd = """
193193
""", description = "Remove distribution artifacts from previous builds" }
194194

195195
# Check wheel files with twine
196-
check_pypi = { cmd = "twine check dist/pymomentum_*.whl", description = "Check wheel files with twine" }
196+
wheel_check = { cmd = "twine check dist/pymomentum_*.whl", description = "Check wheel files with twine" }
197197

198198
# Test wheel imports in a clean environment using uv
199-
test_pypi_wheel = { cmd = "python scripts/test_wheel.py", description = "Test wheel imports in a clean virtual environment using uv" }
199+
wheel_test = { cmd = "python scripts/test_wheel.py", description = "Test wheel imports in a clean virtual environment using uv" }
200200

201201
# Publish to TestPyPI for testing
202-
publish_pypi_test = { cmd = "twine upload --repository testpypi --skip-existing dist/pymomentum_*.whl", depends-on = [
203-
"check_pypi",
204-
"test_pypi_wheel",
202+
wheel_publish_testpypi = { cmd = "twine upload --repository testpypi --skip-existing dist/pymomentum_*.whl", depends-on = [
203+
"wheel_check",
204+
"wheel_test",
205205
], description = "Publish to TestPyPI for testing" }
206206

207207
# CPU-specific PyPI tasks - build CPU wheels (default)
208208
# CPU environments (py312, py313) use these tasks
209-
build_wheels = { cmd = """
209+
wheel_build_cibw = { cmd = """
210210
cp $(which pixi) pixi_bin && \
211211
python -c "import sys, os, shutil; py_ver = str(sys.version_info.major) + str(sys.version_info.minor); os.makedirs('.tmp', exist_ok=True); shutil.copy('pyproject.toml', '.tmp/pyproject-backup.toml'); shutil.copy(f'pyproject-pypi-cpu-py{py_ver}.toml', 'pyproject.toml')" && \
212-
(if [ -d build ]; then mv build build_backup; fi) && \
213212
cibuildwheel --platform linux --output-dir dist; \
214213
STATUS=$?; \
215-
(if [ -d build_backup ]; then mv build_backup build; fi) && \
216214
python -c "import shutil; shutil.move('.tmp/pyproject-backup.toml', 'pyproject.toml')" && \
217215
rm pixi_bin && \
218216
exit $STATUS
219217
""", env = { CIBW_CONTAINER_ENGINE = "podman", CIBW_CONTAINER_OPTIONS = "--volume $HOME/.cache/rattler:/root/.cache/rattler" }, depends-on = [
220218
"generate_pyproject",
221219
], description = "Build CPU wheels using cibuildwheel (uses podman)" }
222220

223-
build_pypi_wheel = { cmd = """
221+
wheel_build = { cmd = """
224222
python -c "import sys, os, shutil; py_ver = str(sys.version_info.major) + str(sys.version_info.minor); os.makedirs('.tmp', exist_ok=True); shutil.copy('pyproject.toml', '.tmp/pyproject-backup.toml'); shutil.copy(f'pyproject-pypi-cpu-py{py_ver}.toml', 'pyproject.toml')" && \
225223
pip wheel . --no-deps --no-build-isolation --wheel-dir=dist && \
226224
python -c "import shutil; shutil.move('.tmp/pyproject-backup.toml', 'pyproject.toml')"
227225
""", depends-on = [
228226
"generate_pyproject",
229227
], description = "Build CPU wheel for current Python version" }
230228

231-
build_pypi_repair = { cmd = """
229+
wheel_repair = { cmd = """
232230
auditwheel repair \
233231
--exclude 'libtorch*.so' --exclude 'libc10*.so' --exclude 'libmkl*.so' \
234232
dist/pymomentum_cpu-*-linux_x86_64.whl -w dist/ && \
235233
rm dist/pymomentum_cpu-*-linux_x86_64.whl || \
236234
echo "Skip auditwheel (non-Linux)"
237235
""", description = "Repair CPU wheel with auditwheel (Linux only)" }
238236

239-
publish_pypi = { cmd = "twine upload --repository pymomentum-cpu dist/pymomentum_cpu-*.whl", depends-on = [
240-
"check_pypi",
241-
"test_pypi_wheel",
237+
wheel_publish = { cmd = "twine upload --repository pymomentum-cpu dist/pymomentum_cpu-*.whl", depends-on = [
238+
"wheel_check",
239+
"wheel_test",
242240
], description = "Publish CPU wheels to PyPI" }
243241

244242
#===========
@@ -496,15 +494,15 @@ dependencies = { cuda-toolkit = ">=12.9.0,<13", nvtx-c = ">=3.1.1", cuda-version
496494

497495
[feature.py312-cuda129.tasks]
498496
# GPU-specific PyPI tasks - build GPU wheels
499-
build_pypi_wheel = { cmd = """
497+
wheel_build = { cmd = """
500498
python -c "import os, shutil; os.makedirs('.tmp', exist_ok=True); shutil.copy('pyproject.toml', '.tmp/pyproject-backup.toml'); shutil.copy('pyproject-pypi-gpu.toml', 'pyproject.toml')" && \
501499
pip wheel . --no-deps --no-build-isolation --wheel-dir=dist && \
502500
python -c "import shutil; shutil.move('.tmp/pyproject-backup.toml', 'pyproject.toml')"
503501
""", depends-on = [
504502
"generate_pyproject",
505503
], description = "Build GPU wheel for current Python version" }
506504

507-
build_pypi_repair = { cmd = """
505+
wheel_repair = { cmd = """
508506
auditwheel repair \
509507
--exclude 'libtorch*.so' --exclude 'libc10*.so' \
510508
--exclude 'libcu*.so*' --exclude 'libnv*.so*' --exclude 'libmkl*.so' \
@@ -513,9 +511,9 @@ build_pypi_repair = { cmd = """
513511
echo "Skip auditwheel (non-Linux)"
514512
""", description = "Repair GPU wheel with auditwheel (Linux only)" }
515513

516-
publish_pypi = { cmd = "twine upload --repository pymomentum-gpu dist/pymomentum_gpu-*.whl", depends-on = [
517-
"check_pypi",
518-
"test_pypi_wheel",
514+
wheel_publish = { cmd = "twine upload --repository pymomentum-gpu dist/pymomentum_gpu-*.whl", depends-on = [
515+
"wheel_check",
516+
"wheel_test",
519517
], description = "Publish GPU wheels to PyPI" }
520518

521519
[feature.py313-cuda129]
@@ -525,15 +523,15 @@ dependencies = { cuda-toolkit = ">=12.9.0,<13", nvtx-c = ">=3.1.1", cuda-version
525523

526524
[feature.py313-cuda129.tasks]
527525
# GPU-specific PyPI tasks - build GPU wheels
528-
build_pypi_wheel = { cmd = """
526+
wheel_build = { cmd = """
529527
python -c "import os, shutil; os.makedirs('.tmp', exist_ok=True); shutil.copy('pyproject.toml', '.tmp/pyproject-backup.toml'); shutil.copy('pyproject-pypi-gpu.toml', 'pyproject.toml')" && \
530528
pip wheel . --no-deps --no-build-isolation --wheel-dir=dist && \
531529
python -c "import shutil; shutil.move('.tmp/pyproject-backup.toml', 'pyproject.toml')"
532530
""", depends-on = [
533531
"generate_pyproject",
534532
], description = "Build GPU wheel for current Python version" }
535533

536-
build_pypi_repair = { cmd = """
534+
wheel_repair = { cmd = """
537535
auditwheel repair \
538536
--exclude 'libtorch*.so' --exclude 'libc10*.so' \
539537
--exclude 'libcu*.so*' --exclude 'libnv*.so*' --exclude 'libmkl*.so' \
@@ -542,9 +540,9 @@ build_pypi_repair = { cmd = """
542540
echo "Skip auditwheel (non-Linux)"
543541
""", description = "Repair GPU wheel with auditwheel (Linux only)" }
544542

545-
publish_pypi = { cmd = "twine upload --repository pymomentum-gpu dist/pymomentum_gpu-*.whl", depends-on = [
546-
"check_pypi",
547-
"test_pypi_wheel",
543+
wheel_publish = { cmd = "twine upload --repository pymomentum-gpu dist/pymomentum_gpu-*.whl", depends-on = [
544+
"wheel_check",
545+
"wheel_test",
548546
], description = "Publish GPU wheels to PyPI" }
549547

550548
#==============

pyproject-pypi.toml.j2

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ cmake.args = [
9191
"-DCMAKE_CXX_SCAN_FOR_MODULES=OFF",
9292
"-DMOMENTUM_USE_SYSTEM_RERUN_CPP_SDK=OFF",
9393
"-DMOMENTUM_USE_SYSTEM_PYBIND11=ON",
94+
"-DCMAKE_DISABLE_FIND_PACKAGE_Arrow=ON",
9495
]
9596
minimum-version = "0.10"
9697
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
@@ -117,6 +118,7 @@ cmake.args = [
117118
"-DMOMENTUM_USE_SYSTEM_RERUN_CPP_SDK=OFF",
118119
"-DMOMENTUM_USE_SYSTEM_PYBIND11=ON",
119120
]
121+
# CMAKE_PREFIX_PATH is set by cibuildwheel environment
120122

121123
[[tool.scikit-build.overrides]]
122124
if.platform-system = "^linux"
@@ -130,8 +132,8 @@ cmake.args = [
130132
"-DCMAKE_CXX_SCAN_FOR_MODULES=OFF",
131133
"-DMOMENTUM_USE_SYSTEM_RERUN_CPP_SDK=ON",
132134
"-DMOMENTUM_USE_SYSTEM_PYBIND11=ON",
133-
"-DCMAKE_C_COMPILER=/tmp/build_env/.pixi/envs/default/bin/x86_64-conda-linux-gnu-gcc",
134-
"-DCMAKE_CXX_COMPILER=/tmp/build_env/.pixi/envs/default/bin/x86_64-conda-linux-gnu-g++",
135+
# Use manylinux container's compiler (gcc-toolset-12) instead of pixi's GCC 14.3
136+
# to ensure manylinux_2_28 compatibility (requires GLIBCXX <= 3.4.24, CXXABI <= 1.3.11)
135137
"-DCMAKE_CXX_FLAGS=-static-libstdc++ -static-libgcc",
136138
"-DCMAKE_C_FLAGS=-static-libgcc",
137139
]
@@ -180,7 +182,6 @@ version_file = "pymomentum/_version.py"
180182

181183
{% if variant == "cpu" %}
182184
[tool.cibuildwheel]
183-
build-frontend = "pip; args: --no-build-isolation"
184185
build = "cp312-* cp313-*"
185186
build-verbosity = 3
186187
skip = "pp* *-musllinux*"
@@ -197,15 +198,6 @@ cp pyproject-pypi-cpu-py${VER}.toml pyproject.toml
197198
# We need torch to link against libtorch
198199
# Use the same constraints as in the generated pyproject.toml
199200
pip install "torch>=2.8.0,<2.9" --index-url https://download.pytorch.org/whl/cpu
200-
201-
# Install build backend dependencies manually since we use --no-build-isolation
202-
pip install scikit-build-core pybind11 setuptools-scm cmake ninja
203-
204-
echo "Checking compilers in pixi env:"
205-
ls -l /tmp/build_env/.pixi/envs/default/bin/x86_64-conda-linux-gnu-gcc
206-
ls -l /tmp/build_env/.pixi/envs/default/bin/x86_64-conda-linux-gnu-g++
207-
echo "CC=$CC"
208-
echo "CXX=$CXX"
209201
"""
210202
test-command = "python {project}/scripts/run_test.py"
211203
test-requires = ["numpy", "scipy"]
@@ -231,12 +223,50 @@ cp {project}/pixi.toml {project}/pixi.lock {project}/README.md {project}/LICENSE
231223
cd /tmp/build_env
232224
pixi install -e default
233225
"""
234-
environment = { PATH = "$HOME/.pixi/bin:$PATH", CMAKE_PREFIX_PATH = "/tmp/build_env/.pixi/envs/default", MOMENTUM_BUILD_WITH_FBXSDK = "OFF", CC = "/tmp/build_env/.pixi/envs/default/bin/x86_64-conda-linux-gnu-gcc", CXX = "/tmp/build_env/.pixi/envs/default/bin/x86_64-conda-linux-gnu-g++" }
235-
repair-wheel-command = "export LD_LIBRARY_PATH=/tmp/build_env/.pixi/envs/default/lib:$LD_LIBRARY_PATH && echo 'Starting auditwheel...' && auditwheel -v repair -w {dest_dir} {wheel} --exclude libtorch_cpu.so --exclude libtorch.so --exclude libc10.so --exclude libmkl_intel_lp64.so --exclude libmkl_gnu_thread.so --exclude libmkl_core.so --exclude libmkl_def.so --exclude libmkl_rt.so --exclude libmkl_sequential.so --exclude libmkl_intel_thread.so --exclude libQt6Core.so --exclude libQt6Gui.so --exclude libQt6Widgets.so --exclude libQt6OpenGL.so --exclude libQt6Network.so --exclude libQt6Qml.so --exclude libQt6Quick.so --exclude libicui18n.so --exclude libicuuc.so --exclude libicudata.so --exclude libLLVM-18.so --exclude libvulkan.so --exclude libdrm.so 2>&1 | tee /project/auditwheel.log || (echo 'Auditwheel failed.' && exit 1)"
226+
# Use manylinux container's gcc-toolset-12 compiler for manylinux_2_28 compatibility
227+
# Pixi provides dependencies (headers/libs) but we use the container's compiler
228+
environment = { PATH = "$HOME/.pixi/bin:$PATH", CMAKE_PREFIX_PATH = "/tmp/build_env/.pixi/envs/default", CONDA_PREFIX = "/tmp/build_env/.pixi/envs/default", MOMENTUM_BUILD_WITH_FBXSDK = "OFF" }
229+
repair-wheel-command = """
230+
# Bundle libraries with auditwheel using linux_x86_64 (no ABI checks)
231+
# Then rename to manylinux_2_28_x86_64 for pip compatibility
232+
# This workaround is needed because conda-forge libraries use GCC 14+ with CXXABI_1.3.15 symbols
233+
# which are incompatible with manylinux_2_28's max CXXABI_1.3.11
234+
export LD_LIBRARY_PATH=/tmp/build_env/.pixi/envs/default/lib:$LD_LIBRARY_PATH
235+
236+
echo 'Step 1: Bundling libraries with auditwheel (linux_x86_64)...'
237+
auditwheel repair -w /tmp/linux_wheel {wheel} \
238+
--plat linux_x86_64 \
239+
--exclude 'libtorch*.so*' \
240+
--exclude 'libc10*.so*' \
241+
--exclude 'libmkl*.so*' \
242+
--exclude 'libgomp*.so*' || (echo 'Auditwheel failed.' && exit 1)
243+
244+
echo 'Step 2: Renaming wheel to manylinux_2_28_x86_64...'
245+
LINUX_WHEEL=$(ls /tmp/linux_wheel/*.whl | head -1)
246+
WHEEL_NAME=$(basename "$LINUX_WHEEL")
247+
NEW_WHEEL_NAME=$(echo "$WHEEL_NAME" | sed 's/linux_x86_64/manylinux_2_28_x86_64/')
248+
cp "$LINUX_WHEEL" {dest_dir}/$NEW_WHEEL_NAME
249+
echo "Created $NEW_WHEEL_NAME"
250+
echo "Note: This wheel requires glibc 2.28+ and libstdc++ from GCC 11+"
251+
"""
236252

237253
[tool.cibuildwheel.macos]
254+
# Disable build isolation to ensure CMAKE_PREFIX_PATH is properly passed to CMake
255+
build-frontend = "pip; args: --no-build-isolation"
256+
# Install build dependencies before building (since we disabled build isolation)
257+
before-build = """
258+
pip install scikit-build-core pybind11 setuptools-scm
259+
pip install "torch>=2.8.0,<2.9" --index-url https://download.pytorch.org/whl/cpu
260+
"""
238261
environment = { CMAKE_PREFIX_PATH = "{project}/.pixi/envs/default", MOMENTUM_BUILD_WITH_FBXSDK = "OFF" }
239262

240263
[tool.cibuildwheel.windows]
264+
# Disable build isolation to ensure CMAKE_PREFIX_PATH is properly passed to CMake
265+
build-frontend = "pip; args: --no-build-isolation"
266+
# Install build dependencies before building (since we disabled build isolation)
267+
before-build = """
268+
pip install scikit-build-core pybind11 setuptools-scm
269+
pip install "torch>=2.8.0,<2.9" --index-url https://download.pytorch.org/whl/cpu
270+
"""
241271
environment = { CMAKE_PREFIX_PATH = "{project}/.pixi/envs/default", MOMENTUM_BUILD_WITH_FBXSDK = "OFF" }
242272
{% endif %}

0 commit comments

Comments
 (0)