Wheel Packaging Demo
The simplest possible wheel packaging demo. Builds a single .so and
wraps it in a py3-none-any wheel using setuptools.
Wheel Demo
Interface
# Python dict format equivalent of arraysum.c2py
{
"module": "_arraysum",
"source": ["arraysum.c"],
"functions": [
{
"py_sig": "array_sum(a: buffer, b: buffer, result: buffer) -> int",
"checks": [
"a.format == 'd'",
"b.format == 'd'",
"result.format == 'd'",
"a.n == b.n",
"a.n == result.n",
],
"c_overloads": [
{
"sig": "array_sum(const double *a, const double *b, double *result, int n) -> int",
"map": {
"a": "a.ptr",
"b": "b.ptr",
"result": "result.ptr",
"n": "a.n",
},
},
],
},
],
}
C Source
/* arraysum.c - element-wise addition of double arrays */
extern int array_sum(const double *a, const double *b, double *result, int n);
int array_sum(const double *a, const double *b, double *result, int n) {
int i;
for (i = 0; i < n; i++) {
result[i] = a[i] + b[i];
}
return n;
}
Build
Compile:
$ cc -shared -fPIC c2py23/runtime/c2py_runtime.c _arraysum_wrapper.c arraysum.c -I c2py23/runtime -o _arraysum.so -ldl -lm
See docs/building for cmake, meson, and setuptools options.
Build Script
#!/usr/bin/env bash
# build.sh - Build a c2py23 wheel using the c2py_loader naming convention.
#
# _arraysum.c2py23-linux_x86_64.so (on x86_64)
# _arraysum.c2py23-linux_aarch64.so (cross-compiled)
# _arraysum.c2py23-linux_ppc64le.so (cross-compiled)
#
# Usage:
# bash build.sh # build for host arch, produce wheel
# bash build.sh --arch linux_aarch64 # cross-compile for aarch64
# bash build.sh --pack-only # assemble wheel from pre-built .so files
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
MODULE="arraysum"
PKG_DIR="$MODULE"
C2PY_MODULE="_$MODULE"
# --- parse args ---
ARCH=""
PACK_ONLY=false
while [ $# -gt 0 ]; do
case "$1" in
--arch) ARCH="$2"; shift 2 ;;
--pack-only) PACK_ONLY=true; shift ;;
*) echo "Unknown: $1"; exit 1 ;;
esac
done
# --- find python ---
PY="${PYTHON:-python3}"
if ! command -v "$PY" >/dev/null 2>&1; then
# manylinux2014: Pythons are /opt/python/cp3X-cp3X/bin/python3.X
for candidate in python3.12 python3.11 python3.10 python3.9 python3; do
if command -v "$candidate" >/dev/null 2>&1; then
PY="$candidate"; break
fi
done
fi
# --- detect platform ---
if [ -z "$ARCH" ]; then
_OS=$("$PY" -c "import sys; p=sys.platform; print('linux' if p.startswith('linux') else p)")
_ARCH=$("$PY" -c "import platform; m=platform.machine(); print('x86_64' if m=='AMD64' else m)")
ARCH="${_OS}_${_ARCH}"
fi
echo "=== c2py23 wheel demo ==="
echo "Target: $ARCH"
if [ "$PACK_ONLY" = false ]; then
# --- generate wrapper ---
echo ""
echo "1. Generating wrapper..."
"$PY" -m c2py23.cli generate "$MODULE.c2py" -o "${C2PY_MODULE}_wrapper.c"
# --- find runtime ---
RUNTIME_DIR=$("$PY" -c "import c2py23, os; print(os.path.join(os.path.dirname(c2py23.__file__), 'runtime'))")
# --- pick compiler ---
CC="${CC:-gcc}"
case "$ARCH" in
linux_aarch64) CC="${CC:-aarch64-linux-gnu-gcc}" ;;
linux_ppc64le) CC="${CC:-powerpc64le-linux-gnu-gcc}" ;;
win_amd64) CC="${CC:-x86_64-w64-mingw32-gcc}" ;;
esac
CFLAGS="${CFLAGS:--O3 -Wall -Werror -fPIC}"
# --- compile ---
SO_NAME="${C2PY_MODULE}.c2py23-${ARCH}.so"
echo ""
echo "2. Compiling $SO_NAME..."
echo " $CC -shared $CFLAGS -I $RUNTIME_DIR ... -o $PKG_DIR/$SO_NAME"
$CC -shared $CFLAGS \
-I "$RUNTIME_DIR" \
-o "$PKG_DIR/$SO_NAME" \
"${C2PY_MODULE}_wrapper.c" \
"$MODULE.c" \
"$RUNTIME_DIR/c2py_runtime.c" \
-ldl -lm
echo " -> $PKG_DIR/$SO_NAME"
fi
# --- assemble wheel ---
echo ""
echo "3. Building wheel..."
"$PY" -m pip install -q wheel 2>/dev/null || true
"$PY" setup.py bdist_wheel 2>&1 | tail -3
echo ""
echo "=== Done ==="
ls -la dist/*.whl 2>/dev/null || echo "(no wheel produced)"
echo ""
echo "Wheel tag: py3-none-any"
echo "To test: pip install dist/*.whl && python3 -c 'import $MODULE; print($MODULE.array_sum)'"
How It Works
build.sh:
1. runs c2py23 to produce the wrapper C file
2. Compiles the .so with gcc -shared (platform-tagged filename)
3. Copies it into the arraysum/ package directory
4. Builds the wheel with python3 -m build
The setup.py overrides bdist_wheel.get_tag() to produce a
py3-none-any wheel. .so files are listed as package_data, not
ext_modules, so EXT_SUFFIX is never applied.