A PEP 517 build backend that uses zig cc (via the ziglang Python package) to compile C/C++ extensions for Python.
This backend allows you to easily compile native Python extensions without needing a pre-installed C compiler on the system, as it leverages the portable Zig compiler toolchain distributed via PyPI.
When you build your project (e.g., using pip or build), this backend:
- Reads your
pyproject.tomlconfiguration. - Identifies the source files specified in
[tool.zigcc-build]. - Invokes
zig cc(provided by theziglangpackage) to compile these sources into a native extension module (.pydon Windows,.soon Linux/macOS). - Packages the result into a standard Python Wheel.
In your pyproject.toml, specify zigcc-build as the build backend.
Option A: Using from PyPI (if published)
[build-system]
requires = ["zigcc-build"]
build-backend = "zigcc_build"Option B: Using directly from GitHub
[build-system]
requires = ["zigcc-build @ git+https://github.com/jrialland/zigcc-build.git"]
build-backend = "zigcc_build"Define your extension module settings in the [tool.zigcc-build] table.
[tool.zigcc-build]
# The name of the compiled extension module (optional).
# Defaults to the project name (with dashes replaced by underscores).
module-name = "my_extension"
# List of source files to compile (C or C++).
sources = ["src/main.c", "src/utils.c"]
# List of additional include directories (optional).
include-dirs = ["include"]
# List of compiler macros/defines (optional).
# Translates to -Dname or -Dname=value
defines = ["MY_MACRO=1", "DEBUG"]
# List of library directories (optional).
# Translates to -Lpath
library-dirs = ["libs"]
# List of libraries to link against (optional).
# Translates to -lname
libraries = ["m", "user32"]
# Optional python script to configure the build dynamically
configurer-script = "configure.py"
# List of python packages to include (optional).
# If omitted, the backend will try to auto-discover packages in 'src/' or the root directory.
packages = ["mypackage"]The backend automatically detects and includes pure Python packages.
- If a
srcdirectory exists, it looks for packages insidesrc. - Otherwise, it looks for packages in the project root.
You can also explicitly specify packages to include using the packages option in [tool.zigcc-build].
If you specify a configurer-script, the backend will load this Python script and call a configure(config) function. This allows you to modify the build configuration dynamically (e.g., based on the platform or environment).
Example configure.py:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from zigcc_build.config import ZigCcConfig
def configure(config: "ZigCcConfig"):
# config is a dictionary with keys: 'sources', 'include_dirs', 'defines', 'module_name'
print("Running custom configuration...")
# Add a define dynamically
config["defines"].append("DYNAMIC_MACRO=1")
# Add sources based on platform
import sys
if sys.platform == "win32":
config["sources"].append("src/windows_utils.c")The configuration object passed to configure() follows this TypedDict schema:
class ZigCcConfig(TypedDict):
sources: List[str] # List of source files to compile
include_dirs: List[str] # List of include directories
defines: List[str] # List of compiler macros
library_dirs: List[str] # List of library directories
libraries: List[str] # List of libraries to link against
module_name: str # The name of the extension module
packages: List[str] # List of python packages to includeYou can build your project using standard Python tooling:
# Install the package in editable mode
pip install -e .
# Build a wheel and sdist
python -m buildThe zigcc-build backend fully supports PEP 621 metadata fields defined in your pyproject.toml. All metadata is automatically included in the generated wheel's METADATA file and the source distribution's PKG-INFO file.
- name - Package name
- version - Package version
- description - Short one-line summary
- readme - Long description from README file (supports
.md,.rst,.txt) - requires-python - Python version requirements
- license - License information (text or file)
- authors - List of authors with name and email
- maintainers - List of maintainers with name and email
- keywords - List of keywords for PyPI
- classifiers - List of PyPI classifiers
- urls - Project URLs (Homepage, Repository, Issues, etc.)
- dependencies - Runtime dependencies
- optional-dependencies - Optional dependency groups
[build-system]
requires = ["zigcc-build"]
build-backend = "zigcc_build"
[project]
name = "my-package"
version = "1.0.0"
description = "A package with C extensions"
readme = "README.md"
requires-python = ">=3.12"
license = {text = "MIT"}
authors = [
{name = "Your Name", email = "you@example.com"}
]
keywords = ["c-extension", "zig", "compiler"]
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"numpy>=1.20",
]
[project.urls]
Homepage = "https://github.com/user/my-package"
Repository = "https://github.com/user/my-package"
Issues = "https://github.com/user/my-package/issues"
[tool.zigcc-build]
module-name = "my_extension"
sources = ["src/extension.c"]The backend will automatically generate complete METADATA and PKG-INFO files with all this information, ensuring your packages are fully compliant with PyPI requirements.
Here is a complete example for a project named demo-package:
[build-system]
requires = ["zigcc-build"]
build-backend = "zigcc_build"
[project]
name = "demo-package"
version = "1.0.0"
description = "A demo package built with zigcc"
readme = "README.md"
requires-python = ">=3.7"
[tool.zigcc-build]
module-name = "demo"
sources = ["src/hello.c"]- Python >= 3.7
- The
ziglangpackage (automatically handled by the build system requirements).
This repository includes a demo-project to illustrate how to use zigcc-build.
Since zigcc-build is not yet on PyPI, you need to install it in your environment first.
# From the root of the repository
pip install -e .Navigate to the demo-project directory and build it. We use --no-build-isolation because the backend is installed locally, not from PyPI.
cd demo-project
python -m build --no-isolationInstall the generated wheel and run the tests.
# Install the generated wheel (replace * with the actual version/platform)
pip install dist/demo_package-*.whl --force-reinstall
# Run the tests
python -m pytest testsTo recompile after changing the C source code, simply run the build command again:
python -m build --no-isolation