Cmeel - CMake Wheel Builder
Cmeel is a Python PEP 517/518 build backend that enables building Python wheels directly from CMake projects. It streamlines the packaging of C++/C/Fortran code wrapped for Python, automating much of the build process. It has an active and frequent release cadence, often with multiple patch or minor releases per month.
Warnings
- breaking Cmeel now requires Python 3.9 or newer. Projects using older Python versions will need to upgrade their interpreter to use cmeel v0.58.0 and later.
- gotcha Cmeel is a PEP 517/518 build backend and only supports `pyproject.toml` for project configuration. It does not support legacy `setup.py` files.
- gotcha The main development branch of the cmeel repository was renamed from `master` to `cmeel`.
- gotcha The musllinux wheel standard was updated from `musllinux_1_1` to `musllinux_1_2` in v0.58.0. This may affect compatibility for wheels deployed on specific musl-based Linux distributions, requiring a rebuild for the newer standard.
- gotcha While `cmeel` attempts to configure CMake with the correct Python interpreter using `-DPython_EXECUTABLE` and `-DPython3_EXECUTABLE`, complex or non-standard Python environments can still lead to CMake failing to find Python. This is especially true if `find_package(Python)` is used without specifying a version or components.
Install
-
pip install cmeel
Imports
- cmeel
build-system.build-backend = "cmeel" (in pyproject.toml)
Quickstart
# 1. Create a pyproject.toml file
# pyproject.toml
# ---
# [build-system]
# requires = ["cmeel[build]>=0.59.0"]
# build-backend = "cmeel"
#
# [project]
# name = "my-cmake-project"
# version = "0.1.0"
# description = "A simple project built with CMake and cmeel"
# requires-python = ">=3.9"
# ---
# 2. Create a minimal CMakeLists.txt file
# CMakeLists.txt
# ---
# cmake_minimum_required(VERSION 3.15)
# project(my_cmake_project LANGUAGES CXX)
#
# # Required to find Python headers and libraries for extensions
# find_package(Python 3.9 COMPONENTS Interpreter Development REQUIRED)
#
# # A real project would add sources and build Python extensions here.
# # For this quickstart, we just ensure CMake configures successfully.
# message(STATUS "CMake successfully configured with Python ${Python_VERSION}")
# ---
# 3. Create a package directory and an __init__.py
# mkdir -p src/my_cmake_project
# touch src/my_cmake_project/__init__.py
# ---
# src/my_cmake_project/__init__.py
# ---
# # Minimal __init__.py for a package structure
# __version__ = "0.1.0"
# ---
# 4. Build the wheel
# Run this command in the project root directory:
# python -m build --wheel
import os
import subprocess
def create_file(path, content):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w') as f:
f.write(content)
# Create a temporary directory for the project
project_dir = "cmeel_quickstart_project"
os.makedirs(project_dir, exist_ok=True)
os.chdir(project_dir)
# 1. pyproject.toml
create_file("pyproject.toml", '''
[build-system]
requires = ["cmeel[build]>=0.59.0"]
build-backend = "cmeel"
[project]
name = "my-cmake-project"
version = "0.1.0"
description = "A simple project built with CMake and cmeel"
requires-python = ">=3.9"
''')
# 2. CMakeLists.txt
create_file("CMakeLists.txt", '''
cmake_minimum_required(VERSION 3.15)
project(my_cmake_project LANGUAGES CXX)
find_package(Python 3.9 COMPONENTS Interpreter Development REQUIRED)
message(STATUS "CMake successfully configured with Python ${Python_VERSION}")
''')
# 3. Package directory and __init__.py
create_file("src/my_cmake_project/__init__.py", '# Minimal __init__.py\n__version__ = "0.1.0"\n')
print("Project structure created. Now attempting to build the wheel...")
try:
# 4. Build the wheel
# Use 'python -m build' command for PEP 517/518 backends
result = subprocess.run(["python", "-m", "build", "--wheel"], capture_output=True, text=True, check=True)
print("Build successful!")
print("\n--- Build Output ---")
print(result.stdout)
print("\n--- Build Errors (if any) ---")
print(result.stderr)
print("\nLook for the .whl file in the 'dist/' directory.")
except subprocess.CalledProcessError as e:
print(f"Build failed: {e}")
print(f"Stdout: {e.stdout}")
print(f"Stderr: {e.stderr}")
except FileNotFoundError:
print("Error: 'python -m build' command not found. Please install the 'build' package: pip install build")
finally:
# Clean up (optional, for running in a sandbox)
os.chdir("..")
# import shutil
# shutil.rmtree(project_dir)
# print(f"Cleaned up directory: {project_dir}")