name: devtools description: Developer reference for Apache TVM-FFI. argument-hint: "[lint | cpp | python | docs]"
TVM-FFI Developer Guide
Condensed reference from docs/dev/. Use this when working on the TVM-FFI codebase.
Prerequisites
- Python: 3.9+ (managed via
uv; default virtualenv at.venv) - Compiler: C++17-capable toolchain (GCC/Clang on Linux, Apple Clang on macOS, MSVC on Windows)
- Build tools: CMake 3.18+, Ninja
- Source: Always clone with
--recursive, or rungit submodule update --init --recursive
All Python-related commands below use uv. The default virtual environment is .venv in the repo root.
1. Run Linters and clang-tidy
Pre-commit hooks (primary linting workflow)
Install and register git hooks:
uv tool install pre-commit
pre-commit install # register git hooks (runs automatically before each commit)
Run hooks manually:
pre-commit run --all-files # all hooks on every file
pre-commit run # all hooks on staged files only
pre-commit run <hook-id> --all-files # single hook, e.g. ruff-check, clang-format
Linters by language:
| Language | Formatter | Linter / Type Checker |
|---|---|---|
| Python | ruff (format) |
ruff (lint), ty (type check) |
| C/C++ | clang-format |
clang-tidy (see below) |
| Cython | double-quote-cython-strings |
cython-lint |
| CMake | cmake-format |
cmake-lint |
| Shell | shfmt |
shellcheck |
| YAML | yamllint |
|
| TOML | taplo-format |
|
| Markdown | markdownlint-cli2 |
|
| RST | rstcheck |
Troubleshooting pre-commit:
- Version problems: Ensure pre-commit 2.18.0+ (
pre-commit --version). - Stale cache: Run
pre-commit cleanto clear the hook cache. - Auto-fixed files: Most formatting hooks fix issues in place. Review changes, stage with
git add -u, and commit again.
clang-tidy (separate from pre-commit)
clang-tidy is not a pre-commit hook. It runs as a separate CI job and only checks changed C++ files. To reproduce locally:
# Run on specific files
uv run --no-project --with "clang-tidy==21.1.1" \
python tests/lint/clang_tidy_precommit.py \
--build-dir=build-pre-commit \
--jobs=$(sysctl -n hw.ncpu) \
include/tvm/ffi/c_api.h src/some_file.cc
# Run on all C++ sources
uv run --no-project --with "clang-tidy==21.1.1" \
python tests/lint/clang_tidy_precommit.py \
--build-dir=build-pre-commit \
--jobs=$(sysctl -n hw.ncpu) \
./src/ ./include ./tests
On macOS,
clang-tidyis resolved viaxcrun; the wrapper script handles this automatically. On Linux, replace$(sysctl -n hw.ncpu)with$(nproc).
2. Build and Test the C++ Package
Build the C++ library (no Python)
cmake . -B build_cpp -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build_cpp --parallel --config RelWithDebInfo --target tvm_ffi_shared
cmake --install build_cpp --config RelWithDebInfo --prefix ./dist
After installation:
- Headers:
dist/include/ - Libraries:
dist/lib/
Build and run C++ tests
Set parallel build level first:
export CMAKE_BUILD_PARALLEL_LEVEL=$(sysctl -n hw.ncpu) # macOS
# export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) # Linux
Configure, build, and run:
cmake . -B build_test -DTVM_FFI_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build_test --clean-first --config Debug --target tvm_ffi_tests
ctest -V -C Debug --test-dir build_test --output-on-failure
Key CMake options
| Option | Default | Description |
|---|---|---|
TVM_FFI_BUILD_TESTS |
OFF |
Enable C++ test targets |
TVM_FFI_ATTACH_DEBUG_SYMBOLS |
OFF |
Attach debug symbols in release mode |
TVM_FFI_USE_LIBBACKTRACE |
ON |
Enable libbacktrace |
TVM_FFI_USE_EXTRA_CXX_API |
ON |
Enable extra C++ API in shared lib |
TVM_FFI_BACKTRACE_ON_SEGFAULT |
ON |
Print backtrace on segfault |
CMAKE_EXPORT_COMPILE_COMMANDS |
OFF |
Generate compile_commands.json for clangd |
On Windows, run from a Developer Command Prompt for VS or ensure the MSVC toolchain is on your
PATH.
3. Build and Test the Python Package
Build (editable install)
The Python build uses scikit-build-core which drives CMake to compile the C++ core and Cython extension:
uv pip install --force-reinstall --verbose -e .
--force-reinstallforces a full rebuild.-e(editable) means pure Python changes are reflected immediately without rebuilding.- C++/Cython changes always require re-running this command.
Pass CMake flags via --config-settings:
uv pip install --force-reinstall --verbose -e . \
--config-settings cmake.define.TVM_FFI_ATTACH_DEBUG_SYMBOLS=ON
Verify the install:
uv run python -c "import tvm_ffi; print(tvm_ffi.__version__)"
Update Python stubs
After building (or after C++/Cython reflection changes), regenerate inline type stubs:
uv run tvm-ffi-stubgen python
This updates inline stub blocks (between tvm-ffi-stubgen(begin) / tvm-ffi-stubgen(end)
markers) inside .py files with type annotations derived from the C++ reflection
registry (field types, method signatures, global function schemas).
Run Python tests
Install with test dependencies, then run pytest:
uv pip install --force-reinstall --verbose --group test -e .
uv run pytest -vvs tests/python
Run Rust tests
Rust tests require the Python package to be installed first (Rust FFI bindings link against the built shared library):
cd rust && cargo test
Troubleshooting
- Rebuilding after C++/Cython changes: Re-run
uv pip install --force-reinstall -e .. Editable installs only auto-reflect pure Python changes. - Submodules missing: Run
git submodule update --init --recursivefrom the repo root. - Library not found at import time: Ensure the dynamic loader can find the shared library. Add the
libdirectory toLD_LIBRARY_PATH(Linux),DYLD_LIBRARY_PATH(macOS), orPATH(Windows).
4. Build Documentation Website
Building documentation requires the Python package to be installed first (see section 3).
Interactive build (auto-reload)
Serves locally at http://127.0.0.1:8000 with live reload:
uv run --group docs sphinx-autobuild docs docs/_build/html \
--ignore docs/reference/cpp/generated
One-off build
uv run --group docs sphinx-build -M html docs docs/_build
With C++ API reference (requires Doxygen)
# Install Doxygen
brew install doxygen # macOS
# sudo apt install doxygen # Linux
# Interactive
BUILD_CPP_DOCS=1 uv run --group docs sphinx-autobuild docs docs/_build/html \
--ignore docs/reference/cpp/generated --watch include
# One-off
BUILD_CPP_DOCS=1 uv run --group docs sphinx-build -M html docs docs/_build
With Rust API reference (requires cargo)
# Interactive
BUILD_RUST_DOCS=1 uv run --group docs sphinx-autobuild docs docs/_build/html \
--ignore docs/reference/rust/generated --watch rust
# One-off
BUILD_RUST_DOCS=1 uv run --group docs sphinx-build -M html docs docs/_build
Build all documentation
BUILD_CPP_DOCS=1 BUILD_RUST_DOCS=1 uv run --group docs sphinx-build \
-M html docs docs/_build
Cleanup
rm -rf docs/_build/ docs/reference/python/generated docs/reference/cpp/generated docs/reference/rust/generated