Minimum viable PEP 517 build backend for C extensions.
Project description
Minimum viable PEP 517 build backend for C extensions.
You know how to build your project. just-build knows how to package it. That's the whole deal.
The problem
Every existing Python build backend either wants to own your build system, assumes you're using setuptools extensions, or drags in a dependency tree bigger than your project. There's no option that just says:
"Here are your C files. Build them however you want. I'll ship the result."
just-build is that option.
Zero-config
Got a single C extension in src/mylib/? No configuration needed:
[build-system]
requires = ["just-buildit"]
build-backend = "just_build"
[project]
name = "mylib"
version = "0.1.0"
Run pip install . and just-build will find src/mylib/, compile every .c
file it contains, and ship the result.
Custom build command
For anything more complex, tell it your build command:
[build-system]
requires = ["just-buildit"]
build-backend = "just_build"
[project]
name = "mylib"
version = "0.1.0"
[tool.just-build]
command = "make"
just-build sets five environment variables, calls your command, packages
everything your command writes to $JUST_BUILD_OUTPUT_DIR, and ships the
result.
Environment variables
just-build sets these before calling your command:
| Variable | Example value |
|---|---|
JUST_BUILD_NAME |
mylib |
JUST_BUILD_PYTHON |
/usr/bin/python3.12 |
JUST_BUILD_INCLUDE_DIR |
/usr/include/python3.12 |
JUST_BUILD_OUTPUT_DIR |
/tmp/just-build-xyz/output |
JUST_BUILD_EXT_SUFFIX |
.cpython-312-x86_64-linux-gnu.so |
JUST_BUILD_LDFLAGS |
-shared -fPIC (Linux) / -dynamiclib -undefined dynamic_lookup (macOS) |
JUST_BUILD_LIBS |
`` (Linux/macOS) / -L/ucrt64/lib -lpython3.14 (Windows/MinGW) |
$JUST_BUILD_OUTPUT_DIR is the wheel content root. Write everything your
wheel needs there — extensions, Python sources, data files. just-build
packages the entire directory verbatim, preserving structure.
Examples
Makefile
TARGET := $(JUST_BUILD_OUTPUT_DIR)/$(JUST_BUILD_NAME)$(JUST_BUILD_EXT_SUFFIX)
all: $(TARGET)
$(TARGET):
$(CC) $(JUST_BUILD_LDFLAGS) \
-I$(JUST_BUILD_INCLUDE_DIR) \
src/mylib/mylib.c \
-o $(TARGET) \
$(JUST_BUILD_LIBS)
CMake
[tool.just-build]
command = "make pyext"
pyext:
cmake -B _build -DPython3_EXECUTABLE=$(JUST_BUILD_PYTHON)
cmake --build _build --target mylib
find _build -maxdepth 3 -name "$(JUST_BUILD_NAME)$(JUST_BUILD_EXT_SUFFIX)" \
-exec cp {} $(JUST_BUILD_OUTPUT_DIR)/ \;
.PHONY: pyext
# CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(mylib C)
find_package(Python3 COMPONENTS Development.Module REQUIRED)
Python3_add_library(mylib MODULE src/mylib.c)
set_target_properties(mylib PROPERTIES
OUTPUT_NAME "$ENV{JUST_BUILD_NAME}"
SUFFIX "$ENV{JUST_BUILD_EXT_SUFFIX}"
PREFIX "")
Meson
[tool.just-build]
command = "make pyext"
pyext:
meson setup _build --reconfigure -Dbuildtype=release
meson compile -C _build
find _build -name "*$(JUST_BUILD_EXT_SUFFIX)" \
-exec cp {} $(JUST_BUILD_OUTPUT_DIR)/ \;
# meson.build
project('mylib', 'c')
py = import('python').find_installation()
py.extension_module('mylib', 'src/mylib.c', install: false)
Mixed pure Python + C extension
A package with a Python API wrapping a C core:
src/mylib/
__init__.py # pure Python
utils.py # pure Python
_core.c # C extension
[tool.just-build]
command = "make"
EXT := $(JUST_BUILD_OUTPUT_DIR)/mylib/_core$(JUST_BUILD_EXT_SUFFIX)
all: $(EXT)
cp src/mylib/*.py $(JUST_BUILD_OUTPUT_DIR)/mylib/
$(EXT):
mkdir -p $(JUST_BUILD_OUTPUT_DIR)/mylib
$(CC) $(JUST_BUILD_LDFLAGS) \
-I$(JUST_BUILD_INCLUDE_DIR) \
src/mylib/_core.c \
-o $(EXT) \
$(JUST_BUILD_LIBS)
import mylib loads the Python package; mylib._core is the compiled
extension — both land in the wheel from a single $JUST_BUILD_OUTPUT_DIR.
Wheel repair
just-build automatically runs the right repair tool for your platform:
| Platform | Tool |
|---|---|
| Linux | uvx auditwheel repair |
| macOS | uvx --from delocate delocate-wheel |
| Windows / MinGW | uvx delvewheel repair |
Override or disable repair in your config:
[tool.just-build]
command = "make"
repair = "uvx auditwheel repair --plat manylinux_2_28_x86_64" # custom
# repair = false # skip entirely
Full configuration reference
[tool.just-build]
command = "make" # optional — omit for zero-config src/{package}/ build
package = "my_package" # optional — package directory name when it differs from project name
repair = "uvx ..." # optional — auto-detected by platform, or false to skip
exclude = [ # optional — glob patterns relative to $JUST_BUILD_OUTPUT_DIR
"mypkg/tests/**",
"mypkg/bench/**",
]
__pycache__/, *.pyc, and *.pyo are always excluded.
Requirements
- Python 3.11+
- A compiler (you already have one)
uvfor wheel repair (uvx auditwheel/uvx delocate-wheel/uvx delvewheel)
Running the tests
python -m unittest tests.test_build tests.test_examples -v
No dependencies required. tests.test_build builds real C extensions, verifies
wheel structure, and confirms correct results. tests.test_examples builds each
example in examples/ end-to-end; CMake and Meson tests skip gracefully if
those tools are not installed.
Bootstrapping (offline or pre-release)
just-build is a pure Python package with no dependencies. If you need it before it can be fetched from PyPI — air-gapped environment, initial release bootstrap, or simply testing a local change — add the source directly to your path:
git clone https://github.com/just-buildit/just-build.git
export PYTHONPATH=/path/to/just-build/src:$PYTHONPATH
Then use your build frontend with --no-isolation so it picks up the local
copy:
pip install --no-build-isolation .
# or
python -m build --wheel --no-isolation
# or
uv build --no-build-isolation
No build step, no compiler, no install required. src/just_build/ is
importable as-is.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file just_buildit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: just_buildit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 11.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b9b97ca0713449ceb1b2baff321a220ac6bbd626a312d4ba11fbf512441b3fb7
|
|
| MD5 |
c1f23c5f4578dfeb277ec61746e3fc08
|
|
| BLAKE2b-256 |
c9132b6fabed7958319bd3fe6798ffdf86e1a155516bd5ee807eabe5746d6c4a
|
Provenance
The following attestation bundles were made for just_buildit-0.1.1-py3-none-any.whl:
Publisher:
release.yml on just-buildit/just-build
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
just_buildit-0.1.1-py3-none-any.whl -
Subject digest:
b9b97ca0713449ceb1b2baff321a220ac6bbd626a312d4ba11fbf512441b3fb7 - Sigstore transparency entry: 1221629543
- Sigstore integration time:
-
Permalink:
just-buildit/just-build@236aad9bf6b630edf4ac9c4a90d6e2229b0a05cd -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/just-buildit
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@236aad9bf6b630edf4ac9c4a90d6e2229b0a05cd -
Trigger Event:
push
-
Statement type: