Skip to content

Commit bb812fa

Browse files
committed
build: add binary build and transpile script, create different binaries for different os in integration delivery workflow
1 parent fcffdc2 commit bb812fa

File tree

6 files changed

+194
-0
lines changed

6 files changed

+194
-0
lines changed

.github/workflows/integration_delivery.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,88 @@ jobs:
132132
fail_ci_if_error: false
133133
token: ${{ secrets.CODECOV_TOKEN }}
134134

135+
transpile:
136+
name: Transpile to C
137+
needs: dependencies
138+
runs-on: ubuntu-latest
139+
steps:
140+
- uses: actions/checkout@v4
141+
name: Checkout
142+
143+
- uses: actions/setup-python@v5
144+
name: Setup Python
145+
with:
146+
python-version: ${{ env.PYTHON_VERSION }}
147+
148+
- name: Install uv
149+
uses: astral-sh/setup-uv@v3
150+
with:
151+
enable-cache: true
152+
153+
- name: Create virtualenv
154+
run: |
155+
uv venv --system-site-packages
156+
157+
- name: Transpile
158+
run: uv run --frozen poe transpile
159+
160+
- name: Collect C Source
161+
run: |
162+
mkdir -p c_source
163+
find redux -name "*.c" -exec cp --parents {} c_source \;
164+
# Extract version for binary build
165+
git fetch --prune --unshallow
166+
git fetch --depth=1 origin +refs/tags/*:refs/tags/*
167+
uvx hatch version > c_source/VERSION
168+
169+
- name: Upload C Source
170+
uses: actions/upload-artifact@v4
171+
with:
172+
name: c-source
173+
path: c_source
174+
include-hidden-files: true
175+
176+
build_wheels:
177+
name: Build Wheels
178+
needs: transpile
179+
runs-on: ${{ matrix.os }}
180+
strategy:
181+
matrix:
182+
os: [ubuntu-latest, macos-latest, windows-latest]
183+
steps:
184+
- uses: actions/checkout@v4
185+
name: Checkout
186+
187+
- uses: actions/download-artifact@v4
188+
with:
189+
name: c-source
190+
path: .
191+
192+
- name: Swap Build System
193+
run: |
194+
mv pyproject_binary.toml pyproject.toml
195+
mv setup_binary.py setup.py
196+
shell: bash
197+
198+
- name: Read Version
199+
id: version
200+
shell: bash
201+
run: |
202+
ls -R
203+
echo "VERSION=$(cat VERSION)" >> "$GITHUB_ENV"
204+
echo "Read version: $(cat VERSION)"
205+
206+
- name: Build wheels
207+
uses: pypa/[email protected]
208+
env:
209+
CIBW_BUILD: cp311-* cp312-* cp313-* cp314-*
210+
CIBW_ENVIRONMENT: PRETEND_VERSION=${{ env.VERSION }}
211+
212+
- uses: actions/upload-artifact@v4
213+
with:
214+
name: wheels-${{ matrix.os }}
215+
path: ./wheelhouse/*.whl
216+
135217
build:
136218
name: Build
137219
needs:
@@ -215,6 +297,7 @@ jobs:
215297
- lint
216298
- test
217299
- build
300+
- build_wheels
218301
runs-on: ubuntu-latest
219302
environment:
220303
name: pypi
@@ -234,6 +317,12 @@ jobs:
234317
name: binary
235318
path: dist
236319

320+
- uses: actions/download-artifact@v4
321+
with:
322+
pattern: wheels-*
323+
path: dist
324+
merge-multiple: true
325+
237326
- name: Publish to PyPI
238327
uses: pypa/gh-action-pypi-publish@release/v1
239328
with:
@@ -272,6 +361,13 @@ jobs:
272361
name: binary
273362
path: artifacts
274363

364+
- name: Procure Wheels
365+
uses: actions/download-artifact@v4
366+
with:
367+
pattern: wheels-*
368+
path: artifacts
369+
merge-multiple: true
370+
275371
- name: Extract Changelog
276372
id: changelog
277373
run: |

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,9 @@ redux/_version.py
9595

9696
# mypy
9797
.mypy_cache/
98+
99+
# Cython
100+
*.c
101+
*.so
102+
*.pyd
103+
c_source/

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ todo_demo = "todo_demo:main"
6262

6363
[tool.poe.tasks]
6464
lint = "ruff check . --unsafe-fixes"
65+
transpile = "python scripts/transpile.py"
6566
typecheck = "pyright -p pyproject.toml ."
6667
test = "pytest --cov --cov-report=term-missing --cov-report=html --cov-report=xml"
6768
sanity = ["typecheck", "lint", "test"]

pyproject_binary.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["setuptools>=68", "wheel", "cython>=3.0", "hatch-vcs"]
3+
build-backend = "setuptools.build_meta"

scripts/transpile.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ruff: noqa: T201
2+
"""Transpile all python files in redux/ to C files using Cython."""
3+
4+
from pathlib import Path
5+
6+
from Cython.Build import cythonize
7+
8+
9+
def transpile() -> None:
10+
"""Found all python files in redux/ and transpile them to C files using Cython."""
11+
root = Path(__file__).parent.parent
12+
redux_dir = root / 'redux'
13+
14+
# Find all .py files
15+
py_files = list(redux_dir.rglob('*.py'))
16+
17+
source_files = []
18+
19+
print(f'Found {len(py_files)} Python files to transpile.')
20+
21+
for py_file in py_files:
22+
# Skip _version.py as it is often generated/problematic
23+
if py_file.name == '_version.py':
24+
continue
25+
26+
source_files.append(str(py_file))
27+
28+
# Cythonize in place to produce .c files next to .py files
29+
# We use language_level=3 for Python 3
30+
# keep_path=True is implied by passing full paths often, but good to ensure
31+
# directory structure is respected in modules if we were building them.
32+
# Here we just generate sources.
33+
cythonize(source_files, language_level='3', force=True)
34+
print('Transpilation complete.')
35+
36+
37+
if __name__ == '__main__':
38+
transpile()

setup_binary.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# ruff: noqa: T201
2+
"""Setup script for building the binary extension of python-redux."""
3+
4+
import os
5+
import sys
6+
from pathlib import Path
7+
8+
from setuptools import Extension, find_packages, setup
9+
10+
# Add root to path to import version
11+
root = Path(__file__).parent
12+
sys.path.insert(0, str(root))
13+
14+
try:
15+
from version import get_version
16+
17+
version = get_version()
18+
except ImportError:
19+
# If version.py or dependencies fail, fallback or fail.
20+
# In CI, dependencies should be installed.
21+
print('Warning: Could not import get_version from version.py')
22+
version = '0.0.0.dev0'
23+
24+
25+
def get_extensions() -> list[Extension]:
26+
"""Dynamically find all C extensions in the redux/ directory."""
27+
extensions: list[Extension] = []
28+
# Find all .c files in redux/
29+
for path in (root / 'redux').rglob('*.c'):
30+
# Construct module name: redux/store.c -> redux.store
31+
# relatives_to root
32+
rel_path = path.relative_to(root)
33+
module_name = str(rel_path.with_suffix('')).replace(os.sep, '.')
34+
35+
extensions.append(Extension(module_name, [str(rel_path)]))
36+
return extensions
37+
38+
39+
setup(
40+
name='python-redux',
41+
version=version,
42+
description='Redux implementation for Python (Binary Extension)',
43+
packages=find_packages(include=['redux', 'redux.*']),
44+
ext_modules=get_extensions(),
45+
include_package_data=True,
46+
# Add other metadata as needed matching pyproject.toml
47+
author='Sassan Haradji',
48+
author_email='[email protected]',
49+
url='https://github.com/sassanh/python-redux/',
50+
)

0 commit comments

Comments
 (0)