Setup and Workflow¶
Toolchain¶
| Tool | Notes |
|---|---|
| Rust | rust-toolchain.toml pins the channel (1.97.0 at the time of writing) with rustfmt and clippy; rustup installs it on first use. The CI workflows pin the same version, so bump them together |
protoc |
Required: build.rs compiles the ONNX and CoreML protobuf schemas. Linux apt-get install protobuf-compiler, macOS brew install protobuf, Windows winget install Google.Protobuf |
flatc |
Required for litert-runtime (TFLite schema). CI downloads the flatbuffers release binary; macOS brew install flatbuffers |
| Node.js | WPT corpus fetch and evaluation (scripts/fetch_wpt.mjs, scripts/wpt_bridge/) |
| Python 3 | Only for MkDocs (pip install -r docs/requirements.txt) and scripts/generate_backend_operator_report.py |
| libclang | trtx-runtime: the trtx-sys crate generates bindings with autocxx; set LIBCLANG_PATH if it is not found |
| Graphviz | Optional, for make viz |
| Doxygen and cbindgen | Required for the documentation website and make docs-capi; install Doxygen with your package manager and cbindgen with cargo install cbindgen --locked |
Windows: install the Visual Studio C++ build tools and run git config --system core.longpaths true
before cloning. Backend libraries (ONNX Runtime, TensorRT-RTX, LiteRT) are described in
Backends.
Optional feature prerequisites¶
The default build needs no native runtime beyond the prerequisites above.
| Cargo feature | Additional requirements |
|---|---|
onnx-runtime |
The pinned ONNX Runtime; make onnxruntime-download installs it and the related Make targets configure ORT_DYLIB_PATH |
coreml-runtime |
Xcode and system CoreML on macOS/iOS; see the CoreML guide for deployment requirements and the tvOS dependency caveat |
trtx-runtime, trtx-enterprise |
A compatible NVIDIA GPU and driver, CUDA 13.0, and a loadable TensorRT-RTX runtime |
litert-runtime |
flatc on PATH plus the native LiteRT library provisioned by litert-sys; WPT runs also configure its library search path |
cann-runtime |
An OHOS/Ascend HiAI environment and libcann_shim.so (or CANN_SHIM_PATH) |
webnn-runtime with webnn-wpt-tests |
The wasm32-unknown-unknown target; browser tests additionally need wasm-pack, Node.js, Chrome/Chromium, curl, and unzip |
The mock backend features and dynamic-inputs, native-examples, pollster, and
zstd-cache-compression do not add external runtime prerequisites.
Build and test¶
Use the Makefile targets; they set feature flags and environment variables consistently.
make help prints the same list.
| Target | Effect |
|---|---|
build |
cargo build, no backend features |
test |
cargo fmt, clippy -D warnings, cargo test, operator report drift check |
fmt, fmt-check, lint |
rustfmt (apply / check), clippy only |
clean, clean-all |
cargo clean; also the docs site and coverage output |
onnxruntime-download |
Fetch the pinned ONNX Runtime into target/onnxruntime; export ORT_DYLIB_PATH afterwards |
trtxruntime-download |
Fetch the pinned TensorRT-RTX SDK into target/tensorrt-rtx; test-wpt-trtx configures its build and runtime paths |
run |
Validate examples/sample_graph.json with the CLI |
viz |
Export the sample graph as Graphviz DOT |
onnx, onnx-validate |
Convert the sample graph to ONNX (GRAPH_FILE=... selects another graph); also execute it with ONNX Runtime |
coreml, coreml-validate |
CoreML conversion and execution (macOS) |
benchmark-coreml-kv |
Self-contained attention/cache comparison; optionally set COREML_KV_CONFIG for an external model/reference fixture; see Examples |
test-coreml-kv-benchmark |
Oracle and cache-content regressions for all storage modes, without external fixtures; run in macOS CI |
build-coreml, test-coreml |
Build all targets; run library and ordinary integration tests. COREML_FEATURES=coreml-runtime also checks the build without dynamic inputs |
test-coreml-gather |
Focused active-dimension gather regressions, including scalar indices; TEST_FILTER selects a test |
test-coreml-dtypes |
Focused numeric boundary regressions for typed dispatch and the convenience API; CI runs with and without dynamic inputs |
test-coreml-tensor-reuse |
Retained storage ownership, type conversion and shape regressions; CI runs with and without dynamic inputs |
litert, cann |
LiteRT and CANN conversion of the sample graph |
validate-cann-env, cann-build, cann-device-test |
OpenHarmony toolchain check, cross build, device test through hdc; see CANN |
validate-all-env |
Build, unit tests, ONNX and CoreML validation in one run |
fetch-wpt |
Download the pinned WPT corpus into the cache (WPT_DIR overrides) |
test-wpt |
WPT conformance on ONNX Runtime CPU |
test-wpt-tolerance, test-wpt-tolerance-parity |
Backend-independent comparison regressions and direct upstream JavaScript-helper parity; the latter requires Node.js and a fetched WPT corpus and also runs after successful full ONNX/CoreML WPT targets |
test-wpt-op OP=relu |
One operation; WPT_BACKEND=onnx|trtx|litert|coreml selects the backend |
test-wpt-trtx, test-wpt-litert, test-wpt-coreml, test-wpt-cann |
Per-backend WPT runs (CANN cross-compiles and runs on the device over hdc); test-wpt-report and test-wpt-coreml-report also write the JSON report |
wpt-sync-onnx, wpt-sync-trtx, wpt-sync-litert, wpt-sync-coreml, wpt-sync-cann |
Regenerate snapshots and expected-failure lists |
webnn-chromedriver, test-webnn-wpt-chrome, test-webnn-wpt-chrome-headless |
Browser WebNN graph-build tests in Chrome; see Browser WebNN |
docs-api |
rustdoc with -D warnings |
docs-capi |
Regenerate rustnn.h from src/capi.rs and build the C/C++ Doxygen reference into target/doxygen/html/; see C and C++ API |
docs-build, docs-serve, ci-docs, docs-clean |
MkDocs site into site/ with the Doxygen reference at site/c-api/, live preview, strict mode as CI runs it, remove the site |
docs-backend-ops, docs-backend-ops-check |
Regenerate the operator support report; check it for drift |
coverage, coverage-html, coverage-lcov, coverage-open, coverage-clean |
cargo-llvm-cov reports; see Code Coverage |
CI type-checks every backend. Do the same before pushing when shared code changed:
cargo check
cargo check --features onnx-runtime
cargo check -F trtx-runtime --all-targets
cargo check --features litert-runtime
cargo check --features cann-runtime
cargo check --features coreml-runtime # macOS
cargo test --lib
cargo test --lib --features cann-runtime-mock
Workflow for a change¶
- Branch from
main. - Make the change with its tests. Unit tests live in
#[cfg(test)]modules at the end of each file; converter tests decode the emitted model and assert on its structure. - Run
make test. - If an operation or a converter changed: run the affected WPT cases on every backend you can
(
make test-wpt-op OP=<name>,WPT_BACKEND=<backend>to pick one), regenerate snapshots or expected-failure lists withmake wpt-sync-<backend>, review the diff, and runmake docs-backend-ops. - Update the documentation as described in the Documentation Policy.
Run
make docs-apiandmake ci-docswhen rustdoc or pages changed. - Open a pull request with the template filled in. CI runs formatting, clippy, tests, rustdoc, the operator report drift check, the docs build and the WPT suites.
scripts/install-git-hooks.sh installs a pre-commit hook that runs cargo fmt --check and
clippy when Rust files are staged.
Adding an operation¶
Check Chromium's implementation first. It is the WebNN reference and shows the lowering each backend needs, for example casts for boolean types or decompositions:
- https://chromium.googlesource.com/chromium/src/+/lkgr/services/webnn/ort/graph_builder_ort.cc (ONNX Runtime)
- https://chromium.googlesource.com/chromium/src/+/lkgr/services/webnn/coreml/graph_builder_coreml.cc (CoreML)
- https://chromium.googlesource.com/chromium/src/+/lkgr/services/webnn/tflite/graph_builder_tflite.cc (LiteRT)
Then:
src/operator_options.rs: add theML*Optionsstruct when the spec defines a new dictionary.src/operators.rs: add theOperationvariant with named operand fields, itsop_type()name and thefrom_json_attributesparsing.src/shape_inference.rs: output shape and data type rules, with unit tests.src/mlgraphbuilder.rs: the builder method, through one of theimpl_*_op!macros or explicitly when the signature does not fit.src/webnn_json.rs: import and export mapping for the text and JSON formats.- Converters:
src/converters/onnx.rs,coreml_mlprogram.rs,trtx.rs,litert.rs,cann.rs. A backend that cannot support the operation must reject it explicitly (LiteRT:unsupported_ops; CANN:is_supported_op). - WPT:
make test-wpt-op OP=<name>on every backend you can run, thenmake wpt-sync-<backend>. make docs-backend-ops, and document the builder method and its option fields in source.
Adding a backend¶
- Converter: implement
GraphConverterinsrc/converters/<name>.rsand register it inConverterRegistry::with_defaults. - Backend: implement
MLBackendContext,MLBackendBuilderandListDevicesinsrc/backends/<name>.rs; add theMLBackendGraphvariant and aDisabledContextalias insrc/backends/mod.rs. - Selection: add the
BackendandBackendDevicevariants and the position in the order insrc/backend_selection.rs; extend the fields of theNoBackendAvailableerrors. - Cargo: feature flag, optional dependencies, a mock feature when the hardware is not available
in CI, and a
cargo checkstep in.github/workflows/ci.yml. - Tests: a WPT backend entry in
tests/wpt_conformance/wpt_backend.rs, an expected-failure file or snapshots, atest-wpt-<name>Make target, an integration test undertests/. - Report: a detection rule in
scripts/generate_backend_operator_report.pyand its tests. - Docs: the feature table in
src/lib.rs, selection module docs, and a shared integration page when setup is involved.
Continuous integration¶
| Workflow | Trigger | Content |
|---|---|---|
ci.yml |
push, pull request | Cargo.lock consistency, cargo fmt --check, cargo check per feature (including wasm32, and CoreML on macOS), cargo test --lib on Linux and macOS plus the CANN mock, rustdoc with -D warnings, operator report drift check and generator tests, MkDocs strict build |
wpt-conformance.yml |
push, pull request | WPT suites for ONNX Runtime (Linux), LiteRT (Linux, non-blocking) and CoreML (macOS) |
wpt-conformance-nightly.yml |
schedule | Full WPT run with JSON and HTML reports; publishes the dashboard together with the docs site |
wpt-sync.yml |
weekly, manual | Regenerates snapshots and expected failures against the pinned WPT revision and opens a pull request |
rustnnpt-gate.yml |
pull request | Runs the external rustnnpt conformance runner against the PR's rustnn and enforces a minimum pass rate |
docs.yml, docs-pr.yml |
push to main, pull request |
MkDocs strict build, rustdoc embedded under /api/, WPT report embedded, link check on PRs, deploy to GitHub Pages from main |
publish.yml |
GitHub release, manual | release tag matches Cargo.toml, fmt, clippy, tests, cargo publish |
TensorRT-RTX has no GPU runner. CI compiles it (cargo check -F trtx-runtime --all-targets);
contributors run make test-wpt-trtx locally before and after changing the converter.
Releasing¶
Bump version in Cargo.toml, update Cargo.lock, tag vX.Y.Z and create a GitHub release.
publish.yml fails when the release tag does not match the Cargo.toml version, then runs
formatting, Clippy and library tests before publishing to crates.io. The cargo-c metadata in
Cargo.toml ([package.metadata.capi.*]) carries the version twice more; keep the three in
sync. docs.rs builds the API docs with the features listed under [package.metadata.docs.rs].
Before publishing, run cargo package --list and ensure every Markdown include_str! input is
present, then cargo package to verify the packaged crate builds. The active docs are compiler
inputs; excluding them would break both downstream compilation and docs.rs. Build Rustdoc
from the extracted package as well when changing shared documentation.
Code style¶
cargo fmtandcargo clippy --all-targets -- -D warningsmust pass.- Comments explain non-obvious decisions in one line and use ASCII only. No emojis anywhere in the repository.
- Errors are typed with
thiserror, carry context and areSend + Sync. - Every public item gets rustdoc:
src/lib.rsenables#![warn(missing_docs)]and CI denies warnings. A new module gets a//!header saying what the module owns. Enum variants that are only operand indices (Operation) and error fields described by their message carry an explicit#[allow(missing_docs)].