Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
76ec195
Split pip packaging into halide + halide-bin
alexreinking Jul 23, 2026
0ec8488
Add standalone halide.runtime module for calling AOT kernels without …
Aug 3, 2026
16b1d80
Remove unused tgz and zip packaging scripts
alexreinking Aug 20, 2026
338c3b2
Use SPDX license form
alexreinking Aug 20, 2026
7386d31
Upgrade pre-commit hooks
alexreinking Aug 21, 2026
b5f7e90
Split Python bindings into workspace packages
alexreinking Aug 21, 2026
a7bacf6
Finish standalone Python runtime Buffer bindings
alexreinking Aug 21, 2026
21a1398
Update CI environment sync commands to use --frozen and --no-install-…
alexreinking Aug 21, 2026
8ce420a
Update Python package documentation
alexreinking Aug 21, 2026
b427015
Fix split Python package CI failures
alexreinking Aug 22, 2026
d515c86
Apply pre-commit auto-fixes
halide-ci[bot] Aug 22, 2026
ecbcc96
Avoid dangling-reference compiler warning
alexreinking Aug 22, 2026
6478716
Track the halide-bin Python stub
alexreinking Aug 22, 2026
c4f4001
Fix platform wheel repair commands
alexreinking Aug 22, 2026
34d75b8
Fix relocated Python test image paths
alexreinking Aug 22, 2026
85159d3
Resolve pip LLVM dependency from uv lock
alexreinking Aug 22, 2026
21211bb
Use packaged wheel build dependencies
alexreinking Aug 22, 2026
6b40370
Fix split wheel repair and tests
alexreinking Aug 22, 2026
673d402
Fix packaged wheel dependency discovery
alexreinking Aug 22, 2026
c17da76
Preserve shared package initializer on Windows
alexreinking Aug 22, 2026
4e97b82
Build the bundled runtime for the native target
alexreinking Aug 22, 2026
ad4fe8e
Keep Python runtime bindings header-only
alexreinking Aug 22, 2026
725a51c
Move runtime tests to halide-runtime
alexreinking Aug 22, 2026
43d7e1c
Restore project-wide version bumping
alexreinking Aug 22, 2026
b333d78
Honor disabled Python compiler bindings
alexreinking Aug 22, 2026
ea00208
Propagate runtime output copy failures
alexreinking Aug 22, 2026
192557b
Fix relocated Python tutorial discovery
alexreinking Aug 22, 2026
492d592
Isolate runtime errors between Python threads
alexreinking Aug 22, 2026
3638d03
Describe final split package design
alexreinking Aug 22, 2026
c8b41d9
Construct compiler Buffers from runtime Buffers
alexreinking Aug 22, 2026
69d1851
Expose borrowed runtime Buffer views
alexreinking Aug 22, 2026
ea6229d
Use typed capsules for Python Buffer interop
alexreinking Aug 22, 2026
e76210c
Preserve Python binding exceptions
alexreinking Aug 22, 2026
19e777f
Validate Python buffer metadata
alexreinking Aug 22, 2026
8a706ed
Narrow Python binding conversion catches
alexreinking Aug 22, 2026
34b950e
Test Python API error handling
alexreinking Aug 22, 2026
9c32e5e
Make endian tests host-independent
alexreinking Aug 22, 2026
f50c23e
Use filesystem paths in Python bindings
alexreinking Aug 22, 2026
ad25832
Invalidate Python workspace builds per commit
alexreinking Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ via a `uv`-managed Python virtual env; activate it before building so CMake can
autodetect the LLVM install:

```shell
$ uv sync --group ci-llvm-22 --no-install-project
$ uv sync --frozen --group ci-llvm-22 --no-install-workspace
$ source .venv/bin/activate
```

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:

- name: Sync CI environment
run: |
uv sync --group ci-llvm-22 --group docs --no-install-project
uv sync --frozen --group ci-llvm-22 --group docs --no-install-workspace
echo "${GITHUB_WORKSPACE}/.venv/bin" >> "$GITHUB_PATH"
echo "VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv" >> "$GITHUB_ENV"

Expand Down
354 changes: 263 additions & 91 deletions .github/workflows/pip.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/testing-arm-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
- name: Sync CI environment
run: |
setarch ${{ matrix.arch }} bash -ec "
uv sync --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-project
uv sync --frozen --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-workspace
echo '${GITHUB_WORKSPACE}/.venv/bin' >> '$GITHUB_PATH'
echo 'VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv' >> '$GITHUB_ENV'
"
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/testing-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
run: |
setarch ${{ matrix.arch }} bash -ec "
CC='gcc -m${{ matrix.bits }}' CXX='g++ -m${{ matrix.bits }}' \
uv sync --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-project
uv sync --frozen --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-workspace
echo '${GITHUB_WORKSPACE}/.venv/bin' >> '$GITHUB_PATH'
echo 'VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv' >> '$GITHUB_ENV'
"
Expand Down Expand Up @@ -141,7 +141,7 @@ jobs:

- name: Sync CI environment
run: |
uv sync --group ci-llvm-main --no-install-project
uv sync --frozen --group ci-llvm-main --no-install-workspace
echo "${GITHUB_WORKSPACE}/.venv/bin" >> "$GITHUB_PATH"
echo "VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv" >> "$GITHUB_ENV"

Expand Down Expand Up @@ -226,7 +226,7 @@ jobs:

- name: Sync CI environment
run: |
uv sync --group ci-llvm-main --no-install-project
uv sync --frozen --group ci-llvm-main --no-install-workspace
echo "${GITHUB_WORKSPACE}/.venv/bin" >> "$GITHUB_PATH"
echo "VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv" >> "$GITHUB_ENV"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/testing-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:

- name: Sync CI environment
run: |
uv sync --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-project
uv sync --frozen --python '${{ matrix.python }}' --group '${{ matrix.uv_group }}' --no-install-workspace
echo "${GITHUB_WORKSPACE}/.venv/bin" >> "$GITHUB_PATH"
echo "VIRTUAL_ENV=${GITHUB_WORKSPACE}/.venv" >> "$GITHUB_ENV"

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/testing-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
LLVM_VER=$(uv export --group '${{ matrix.uv_group }}' --no-emit-project \
| awk -F'==' '/^halide-llvm==/ { gsub(/ .*/, "", $2); print $2 }')

uv sync --python '${{ matrix.python }}' --group ci-base --no-install-project
uv sync --frozen --python '${{ matrix.python }}' --group ci-base --no-install-workspace
uv pip install --python-platform i686-pc-windows-msvc --only-binary :all: \
--extra-index-url https://pypi.halide-lang.org/simple/ \
"halide-llvm==${LLVM_VER}"
Expand Down
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ stdout.txt

# Directories
bin/
!python_bindings/halide-bin/src/halide/bin/
!python_bindings/halide-bin/src/halide/bin/__init__.py
distrib/
lib/
lib64/
Expand Down Expand Up @@ -279,5 +281,6 @@ CLAUDE.local.md
!src/autoschedulers/adams2019/included_schedule_file.schedule.h

# TODO: these should become .cmake.in
!packaging/common/HalideConfig.cmake
!packaging/common/HalideCompilerConfig.cmake
!packaging/HalideConfig.cmake
!packaging/HalideCompilerConfig.cmake
!packaging/HalideAutoschedulersConfig.cmake
14 changes: 7 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ repos:
)

- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
rev: v2.4.3
hooks:
- id: codespell
args: [ --ignore-words-list, "CreateOr,implementors,PADD,re-use,re-used,re-using,SME,+sme,subtile,subtiles,tRe" ]
Expand All @@ -63,12 +63,12 @@ repos:
)

- repo: https://github.com/gitleaks/gitleaks
rev: v8.9.0
rev: v8.30.0
hooks:
- id: gitleaks

- repo: https://github.com/google/keep-sorted
rev: v0.8.0
rev: v0.10.0
hooks:
- id: keep-sorted
args: [
Expand All @@ -89,19 +89,19 @@ repos:
- mdformat-shfmt

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.5
rev: v0.16.4
hooks:
- id: ruff-check
- id: ruff-format

- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
rev: v0.11.0.1-1
hooks:
- id: shellcheck
exclude: ^apps/HelloAndroid.*/gradlew$

- repo: https://github.com/maxwinterstein/shfmt-py
rev: v3.12.0.1
rev: v4.0.0
hooks:
- id: shfmt
args: [ -i, "4", -ci, -s, -w ]
Expand All @@ -113,7 +113,7 @@ repos:
- id: taplo-format

- repo: https://github.com/halide/cmtk-pre-commit
rev: v0.1.0
rev: v0.2.1
hooks:
- id: cmtk

Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,8 @@ SOURCE_FILES = \

C_TEMPLATE_FILES = \
CodeGen_C_prologue \
CodeGen_C_vectors
CodeGen_C_vectors \
PythonExtensionRuntime

HTML_TEMPLATE_FILES = \
StmtToHTML_dependencies.html \
Expand Down
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,22 @@ We provide binary wheels on PyPI. Halide provides bindings for C++ and Python.
Even if you only intend to use Halide from C++, pip may be the easiest way to
get a binary build of Halide.

Full releases may be installed with `pip` like so:
Full releases may be installed with `pip` or added to a uv project like so:

```shell
$ pip install halide
$ uv add halide
```

The published Python artifacts are split into three distributions that share the
`halide` import package. The `halide` distribution provides the compiler Python
API and depends on matching versions of `halide-bin` (the compiler, headers,
tools, and CMake package) and `halide-runtime` (the standalone `halide.runtime`
API). Most users should install only `halide`; the package manager installs its
two dependencies automatically. Deployments that only call precompiled AOT
pipelines can instead install `halide-runtime`, avoiding the compiler and LLVM
entirely.

Every commit to `main` is published to our [package index][halide-pypi] as a
development version. If you use [uv](https://docs.astral.sh/uv/), you can pin
the nightly Halide in a project like so:
Expand Down Expand Up @@ -158,13 +168,19 @@ any platform. From the Halide source tree, install with
[uv](https://docs.astral.sh/uv/):

```shell
$ uv sync --group ci-llvm-22 --no-install-project
$ uv sync --frozen --group ci-llvm-22 --no-install-workspace
$ export Halide_LLVM_ROOT=$(halide-llvm --prefix)
```

Replace `22` with the desired LLVM major version (`21`, `22`, `23`, or `main`).
Binary wheels are available for Linux (x86-64, x86-32, AArch64, ARMv7), macOS
(x86-64, ARM64), and Windows (x86-64, x86-32).
The repository root is a uv workspace containing the three Python distributions.
`--no-install-workspace` installs only the development tools and LLVM package
needed for a normal CMake build; `--frozen` uses the committed lockfile without
evaluating or building the workspace packages. To build and install all three
workspace packages into the uv environment instead, run
`uv sync --all-packages`; this includes a full compiler build. Binary wheels are
available for Linux (x86-64, x86-32, AArch64, ARMv7), macOS (x86-64, ARM64), and
Windows (x86-64, x86-32).

On macOS, [Homebrew](https://brew.sh) is also a good option:
`brew install llvm`. On Debian flavors of Linux, the
Expand Down
74 changes: 39 additions & 35 deletions doc/BuildingHalideWithCMake.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Once Python is installed, you can install the Python module dependencies in a
[virtual environment][venv] by running

```shell
$ uv sync
$ uv sync --frozen --no-install-workspace
```

from the root of the repository.
Expand Down Expand Up @@ -237,9 +237,20 @@ dependencies. These are tabulated as constraints in `pyproject.toml` and
resolved to specific versions in `uv.lock`. They may be installed by running:

```shell
$ uv sync --no-install-project
$ uv sync --frozen --no-install-workspace
```

The repository root is a uv workspace for the three published Python
distributions. `--no-install-workspace` installs only the external development
dependencies, leaving the normal CMake build responsible for the Python
bindings. `--frozen` uses the committed lockfile without evaluating or building
the workspace packages.

To build and install the three Python workspace packages instead, run
`uv sync --all-packages`. This performs a full compiler build. The
`--all-packages` option is necessary because the workspace root is a virtual
project and does not itself depend on its members.

## Building Halide

### Basic build
Expand Down Expand Up @@ -484,11 +495,24 @@ $ cmake --install .\build --prefix X:\path\to\Halide-install --config Release
Of course, make sure that you build the corresponding config before attempting
to install it.

## Building Halide with pip
## Building Halide's Python packages

Halide's repository root is a uv workspace, not an installable package. It
contains three distributions under `python_bindings/`: `halide-bin` contains the
compiler, native tools, headers, and CMake package; `halide-runtime` contains
the standalone runtime bindings; and `halide` contains the compiler Python
bindings and depends on matching versions of the other two.

Build them in dependency order with uv:

```shell
$ uv build --package halide-bin
$ uv build --package halide-runtime --find-links dist
$ uv build --package halide --find-links dist
```

Halide also supports installation via the standard Python packaging workflow.
Running `pip install .` at the root of the repository will build a wheel and
install it into the currently active Python environment.
The `--find-links dist` options make the matching, locally built `halide-bin`
available as a build dependency. All artifacts are written to `dist/`.

However, this comes with a few caveats:

Expand All @@ -501,16 +525,16 @@ However, this comes with a few caveats:
3. The generated wheel will likely only work on your system. In particular, it
will not be repaired with `auditwheel` or `delocate`.

Even so, this is a very good method of installing Halide. It supports both
Python and C++ `find_package` workflows.
Even so, this is a useful way to produce local Halide artifacts. The complete
set of wheels supports both Python and C++ `find_package` workflows.

### Using ccache with pip builds
### Using ccache with Python package builds

Because Python's build infrastructure creates temporary CMake build directories,
simply setting `CMAKE_CXX_COMPILER_LAUNCHER` to `ccache` is insufficient to
produce a well-cached build. The following settings should serve as a starting
point to configure your environment (assuming `$PWD` is the repository root) for
using `ccache` with `pip install .`.
using `ccache` with `uv build --package halide-bin`.

```shell
# Point CMake to ccache
Expand All @@ -530,41 +554,21 @@ export CXXFLAGS="$CFLAGS"
# Locate the temporary build beneath $PWD so that CCACHE_BASEDIR works
export TMPDIR=$PWD/build/tmp

# If using uv, don't create a temporary venv
# Build in the workspace environment rather than a temporary environment
export UV_NO_BUILD_ISOLATION=1
```

See the CCache documentation on [compiling in different directories] and on
using [precompiled headers] for more information about these settings. To check
that ccache is working, run,
that ccache is working, build `halide-bin` twice and inspect the cache
statistics:

```shell
$ uv pip install . # first run, populate cache
Resolved 4 packages in 397ms
Built halide @ file:///Users/areinking/dev/Halide
Prepared 1 package in 29.17s
Installed 1 package in 8ms
+ halide==20.0.0.dev87+gf6c939fd3.d20250724 (from file:///Users/areinking/dev/Halide)
$ uv build --package halide-bin # first run, populate cache
$ ccache -z
Statistics zeroed
$ uv pip install . # second run, reload from cache
Resolved 4 packages in 338ms
Built halide @ file:///Users/areinking/dev/Halide
Prepared 1 package in 10.82s
Uninstalled 1 package in 7ms
Installed 1 package in 6ms
~ halide==20.0.0.dev87+gf6c939fd3.d20250724 (from file:///Users/areinking/dev/Halide)
$ uv build --package halide-bin # second run, reuse cached objects
$ ccache -s
Cacheable calls: 1079 / 1080 (99.91%)
Hits: 1079 / 1079 (100.0%)
Direct: 1079 / 1079 (100.0%)
Preprocessed: 0 / 1079 ( 0.00%)
Misses: 0 / 1079 ( 0.00%)
Uncacheable calls: 1 / 1080 ( 0.09%)
Local storage:
Cache size (GB): 2.2 / 30.0 ( 7.24%)
Hits: 1079 / 1079 (100.0%)
Misses: 0 / 1079 ( 0.00%)
```

On this test system (an M3 MacBook Pro), the build is three times faster, with a
Expand Down
8 changes: 4 additions & 4 deletions doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ if (_docs_python_deps_missing)
message(
FATAL_ERROR "WITH_DOCS requires the Python 'myst-parser', 'breathe', 'pydata-sphinx-theme', "
"'sphinxcontrib-video', and 'sphinx-design' packages for the interpreter at ${Python3_EXECUTABLE}. "
"Run: uv sync --group docs"
"Run: uv sync --frozen --group docs --no-install-workspace"
)
endif ()

Expand Down Expand Up @@ -215,7 +215,7 @@ add_custom_command(
##
# Tutorial pages: one MyST page per tutorial/lesson_*.cpp (or .sh), with
# captured gdb/lldb output for interesting statements inlined, plus (where a
# same-stem file exists under python_bindings/tutorial/) the lesson's Python
# same-stem file exists under python_bindings/halide/tutorial/) the lesson's Python
# translation with its own captured output, switchable via a sphinx-design
# tab-set -- see generate_tutorial_website.py's docstring. WITH_DOCS depends
# on WITH_TUTORIALS AND WITH_PYTHON_BINDINGS (top-level CMakeLists.txt), and
Expand Down Expand Up @@ -362,8 +362,8 @@ foreach (_source_file IN LISTS _tutorial_source_files)
)

# Mirrors lesson.py's _python_source(): a same-stem file under
# python_bindings/tutorial/ is that lesson's Python translation.
set(_python_source_file "${Halide_SOURCE_DIR}/python_bindings/tutorial/${_slug}.py")
# python_bindings/halide/tutorial/ is that lesson's Python translation.
set(_python_source_file "${Halide_SOURCE_DIR}/python_bindings/halide/tutorial/${_slug}.py")
if (EXISTS "${_python_source_file}")
list(APPEND _tutorial_lesson_depends "${_python_source_file}")
endif ()
Expand Down
Loading
Loading