Skip to content

CMake Build Demo

Packages a c2py23 module as a py3-none-any wheel using CMake.

Cmake 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

$ c2py23 arraysum.c2py -o _arraysum_wrapper.c

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.

CMake Configuration

cmake_minimum_required(VERSION 3.16)
project(arraysum C)

# --- Platform detection for c2py_loader naming convention ---
execute_process(
  COMMAND python3 -c
    "import sys, platform; p=sys.platform; m=platform.machine(); print('linux_x86_64' if 'linux' in p and m in ('x86_64','AMD64') else f'{p}_{m}')"
  OUTPUT_VARIABLE PLATFORM_KEY
  OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(SO_NAME "_arraysum.c2py23-${PLATFORM_KEY}.so")

# --- Find c2py23 runtime ---
execute_process(
  COMMAND python3 -c
    "import c2py23, os; print(os.path.join(os.path.dirname(c2py23.__file__), 'runtime'))"
  OUTPUT_VARIABLE RUNTIME_DIR
  OUTPUT_STRIP_TRAILING_WHITESPACE
)

# --- Generate wrapper from .c2py ---
add_custom_command(
  OUTPUT _arraysum_wrapper.c
  COMMAND c2py23 generate arraysum.c2py -o _arraysum_wrapper.c
  DEPENDS arraysum.c2py
  COMMENT "c2py23 generate arraysum.c2py"
)

# --- Build the .so with bare name (no EXT_SUFFIX) ---
add_library(_arraysum SHARED
  _arraysum_wrapper.c
  arraysum.c
  ${RUNTIME_DIR}/c2py_runtime.c
)

set_target_properties(_arraysum PROPERTIES
  PREFIX ""
  SUFFIX ".so"
  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/arraysum"
  OUTPUT_NAME "${SO_NAME}"
)

target_include_directories(_arraysum PRIVATE
  ${RUNTIME_DIR}
  ${CMAKE_SOURCE_DIR}
)

target_link_libraries(_arraysum PRIVATE dl m)

Build Script

#!/usr/bin/env bash
# build.sh - Build arraysum wheel using cmake + c2py_loader convention.
set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"

PY="${PYTHON:-python3}"
for c in "$PY" python3.12 python3.11 python3.10 python3.9 python3; do
    command -v "$c" >/dev/null 2>&1 && PY="$c" && break
done

echo "=== arraysum (cmake) wheel build ==="

# 1. Generate wrapper + build .so via cmake
echo "1. Building with cmake..."
cmake -B builddir -S . 2>&1 | tail -1
cmake --build builddir 2>&1 | tail -1

# 2. Assemble wheel
echo ""
echo "2. Building wheel..."
"$PY" setup.py bdist_wheel 2>&1 | tail -3

echo ""
echo "=== Done ==="
ls -la dist/*.whl 2>/dev/null || echo "(no wheel produced)"

How It Works

build.sh: 1. Runs c2py23 to produce the wrapper C file 2. Uses CMake to build _arraysum.c2py23-{os}_{arch}.so 3. Copies the .so into the arraysum/ package directory 4. Builds the wheel with python3 -m build

CMakeLists.txt calls c2py23 via execute_process() and compiles the wrapper + runtime + user C source with add_library().

The package arraysum/__init__.py uses c2py_loader.load_native() to find and load the correct .so at import time.