-
Notifications
You must be signed in to change notification settings - Fork 6
Support building wheels for manylinux and OSX #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| """Wheel smoke test, run from a clean venv by cibuildwheel's test step: | ||
| libcppjit.so must locate libclangCppInterOp relative to its own path (the | ||
| build tree is gone by test time), and the template instantiation plus the | ||
| header check prove the shipped include tree.""" | ||
|
|
||
| import os | ||
|
|
||
| import cppjit | ||
|
|
||
| cppjit.cppdef("int wheel_smoke(int x) { return x + 1; }") | ||
| assert cppjit.gbl.wheel_smoke(41) == 42 | ||
|
|
||
| v = cppjit.gbl.std.vector["int"]() | ||
| v.push_back(7) | ||
| assert v[0] == 7 | ||
|
|
||
| api = os.path.join( | ||
| os.path.dirname(cppjit.__file__), "interop", "include", "cpyrt", "API.h" | ||
| ) | ||
| assert os.path.exists(api), api | ||
|
|
||
| print("wheel smoke OK") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| name: Wheels | ||
|
|
||
| # Build the wheels (cibuildwheel; config in pyproject.toml) and the sdist | ||
| # as artifacts; no index publishing. setup-recipe stages the llvm-wheel | ||
| # toolchain at /opt/llvm; linux mounts it into the build container, the | ||
| # same manylinux_2_28 image the toolchain was built on. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| pull_request: | ||
| paths: | ||
| - '.github/workflows/wheels.yml' | ||
| - '.github/wheel_smoke.py' | ||
| - 'pyproject.toml' | ||
| - 'CMakeLists.txt' | ||
| - 'cmake/**' | ||
| - 'src/interop/**' | ||
| - 'python/cppjit/_cpython_cppjit.py' | ||
| push: | ||
| tags: ['v*'] | ||
| schedule: | ||
| - cron: '30 4 * * 1' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: wheels-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | ||
|
|
||
| jobs: | ||
| wheels: | ||
| name: wheels ${{ matrix.label }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - { os: ubuntu-24.04, label: manylinux-x86_64, arch: x86_64 } | ||
| - { os: macos-26, label: macosx-arm64, arch: arm64 } | ||
| runs-on: ${{ matrix.os }} | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| # ref pins the recipe content the cache key is computed from. | ||
| - uses: compiler-research/ci-workflows/actions/setup-recipe@main | ||
| id: llvm | ||
| with: | ||
| recipe: llvm-wheel | ||
| version: '21.1.8' | ||
| os: ${{ matrix.os }} | ||
| arch: ${{ matrix.arch }} | ||
| ref: b760e4c171961786b7b20e2cc514302df5373eef | ||
|
|
||
| - name: Stage the toolchain at /opt/llvm | ||
| env: | ||
| RECIPE_PATH: ${{ steps.llvm.outputs.path }} | ||
| run: sudo mv "$RECIPE_PATH" /opt/llvm | ||
|
|
||
| - uses: pypa/cibuildwheel@v4.2.0 | ||
|
|
||
| - uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: wheels-${{ matrix.label }} | ||
| path: wheelhouse/*.whl | ||
|
|
||
| sdist: | ||
| name: sdist | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
Comment on lines
+60
to
+71
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are standard PyPA cibuildwheel and sdist actions required to generate the Python wheel |
||
|
|
||
| - run: pipx run build --sdist | ||
|
|
||
| - uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: sdist | ||
| path: dist/*.tar.gz | ||
|
|
||
| # Run the full suite on a plain runner, outside the manylinux | ||
| # container the wheel was built in -- the real relocation boundary. | ||
| test-wheel: | ||
| name: test wheel (full suite) | ||
| needs: wheels | ||
| runs-on: ubuntu-24.04 | ||
| steps: | ||
| - uses: actions/checkout@v7 | ||
|
|
||
| - uses: actions/setup-python@v7 | ||
| with: | ||
| python-version: '3.12' | ||
|
|
||
| - uses: actions/download-artifact@v8 | ||
| with: | ||
| name: wheels-manylinux-x86_64 | ||
| path: wheelhouse | ||
|
|
||
| - name: Assert the manylinux DT_NEEDED whitelist | ||
| # Static toolchain: anything beyond the whitelist is a grafted | ||
| # or leaked dependency. | ||
| run: | | ||
| set -euo pipefail | ||
| allowed='libdl|libpthread|librt|libz|libstdc\+\+|libm|libgcc_s|libc|ld-linux' | ||
| bad=0 | ||
| for whl in wheelhouse/*.whl; do | ||
| dir=$(mktemp -d) | ||
| unzip -q "$whl" -d "$dir" | ||
| while IFS= read -r so; do | ||
| echo "== $whl: ${so#"$dir"/}" | ||
| readelf -d "$so" | awk '/NEEDED/ {print $NF}' | ||
| readelf -d "$so" | awk '/NEEDED/ {print $NF}' \ | ||
| | grep -Ev "^\[($allowed)(\.so|-)" && bad=1 || true | ||
| done < <(find "$dir" -name '*.so*') | ||
| done | ||
| test "$bad" = 0 | ||
|
|
||
| - name: Install the test suite's native deps | ||
| # test_eigen/test_boost need them; the CI cells install the same pair. | ||
| run: sudo apt-get -q update && sudo apt-get -y install libeigen3-dev libboost-dev | ||
|
|
||
| - name: Install the wheel and the test requirements | ||
| run: python -m pip install wheelhouse/cppjit-*cp312*.whl -r requirements.txt | ||
|
|
||
| - name: Smoke the wheel outside pytest | ||
| # pytest captures output at the fd level, so a native abort during | ||
| # collection dies silently; boot the interpreter outside it first. | ||
| run: python -X faulthandler .github/wheel_smoke.py | ||
|
|
||
| - name: Run the test suite against the installed wheel | ||
| env: | ||
| # Match the CI cells, which run the interpreter under C++20. | ||
| CPPINTEROP_EXTRA_INTERPRETER_ARGS: -std=c++20 | ||
| run: | | ||
| cd test | ||
| make -j$(nproc) PYTHON=python | ||
| python -m pytest -ra | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,7 @@ build-backend = "scikit_build_core.build" | |
| name = "cppjit" | ||
| dynamic = ["version"] | ||
| description = "CppJIT: fast and automatic Python-C++ interoperability" | ||
| license = {text = "LBNL BSD"} | ||
| license = "BSD-3-Clause-LBNL" | ||
| requires-python = ">=3.12" | ||
| authors = [ | ||
| {name = "Aaron Jomy"}, | ||
|
|
@@ -21,6 +21,7 @@ maintainers = [ | |
| ] | ||
|
|
||
| [tool.scikit-build] | ||
| minimum-version = "build-system.requires" | ||
| wheel.install-dir = "." | ||
| wheel.packages = ["python/cppjit"] | ||
| cmake.build-type = "Release" | ||
|
|
@@ -30,6 +31,32 @@ provider = "scikit_build_core.metadata.regex" | |
| field = "version" | ||
| input = "python/cppjit/_version.py" | ||
|
|
||
| [tool.cibuildwheel] | ||
| build = ["cp312-*", "cp313-*", "cp314-*"] | ||
| skip = ["*-musllinux*"] | ||
| build-verbosity = 1 | ||
| # The full suite needs a real runner: the image's gcc-8 libstdc++ | ||
| # headers crash the JIT on std::filesystem (test_regression test35). | ||
| # macOS runs it below, linux in the test-wheel job. | ||
| test-sources = ["test", "requirements.txt", ".github/wheel_smoke.py"] | ||
| test-command = "python .github/wheel_smoke.py" | ||
|
|
||
| [tool.cibuildwheel.linux] | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vgvassilev You can see here that the vendored LLVM is reused exactly as build args on the machine that builds the wheel. Those machines are configured in the matrix in wheels.yml: So for every new target platform we can add a new row and extend the wheels infra. I hope this addresses your concern of any redundancy here |
||
| archs = ["x86_64"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about Linux arm? |
||
| manylinux-x86_64-image = "manylinux_2_28" | ||
| # /opt/llvm is staged on the runner by wheels.yml. | ||
| container-engine = { name = "docker", create-args = ["--volume=/opt/llvm:/opt/llvm"] } | ||
| # CMAKE_ARGS reaches the CppInterOp ExternalProject sub-configure. | ||
| environment = { CMAKE_ARGS = "-DLLVM_DIR=/opt/llvm/lib/cmake/llvm -DClang_DIR=/opt/llvm/lib/cmake/clang" } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same to MacOS quesion about the |
||
|
|
||
| [tool.cibuildwheel.macos] | ||
| archs = ["arm64"] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know its being supported less and less, but what about MacOS intel. There is clearly appetite for it by people wanting to use cppjit (see #39 (comment)) |
||
| # Test-suite deps (test_eigen/test_boost). | ||
| before-all = "brew install eigen boost" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do this assume you are using |
||
| test-command = "python -m pip install -r requirements.txt && python .github/wheel_smoke.py && cd test && make -j$(sysctl -n hw.ncpu) PYTHON=python && python -m pytest -ra" | ||
| # 14.0 is the llvm-wheel toolchain's own deployment floor. | ||
| environment = { CMAKE_ARGS = "-DLLVM_DIR=/opt/llvm/lib/cmake/llvm -DClang_DIR=/opt/llvm/lib/cmake/clang", MACOSX_DEPLOYMENT_TARGET = "14.0" } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little confused. Where does this |
||
|
|
||
| [tool.pytest.ini_options] | ||
| testpaths = ["test"] | ||
| pythonpath = ["test"] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we reuse the recipe we created with manylinux