This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new 729f971 [Docs] Add Rust Docs (#160)
729f971 is described below
commit 729f971f272c7a098d032ed80ffcea1aaf580d66
Author: Yixin Dong <[email protected]>
AuthorDate: Sat Oct 18 08:36:03 2025 -0400
[Docs] Add Rust Docs (#160)
This PR adds Rust API docs and a simple tutorial
The rust docstrings still have space for enhancement. We can add more
docstrings later.
---
docs/README.md | 38 +++++++-
docs/conf.py | 55 ++++++++++-
docs/guides/rust_guide.md | 217 ++++++++++++++++++++++++++++++++++++++++++
docs/index.rst | 2 +
docs/reference/rust/index.rst | 40 ++++++++
rust/tvm-ffi-sys/build.rs | 38 ++++++--
6 files changed, 379 insertions(+), 11 deletions(-)
diff --git a/docs/README.md b/docs/README.md
index f0edc73..d790e07 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -56,12 +56,45 @@ sudo apt install doxygen # Linux
Set `BUILD_CPP_DOCS=1` on the desired build command to enable the extra step:
```bash
-# Interactive build
-BUILD_CPP_DOCS=1 uv run --group docs sphinx-autobuild docs docs/_build/html
--ignore docs/reference/cpp/generated
+# Interactive build with auto-rebuild on C++ header changes
+BUILD_CPP_DOCS=1 uv run --group docs sphinx-autobuild docs docs/_build/html
--ignore docs/reference/cpp/generated --watch include
# One-off build
BUILD_CPP_DOCS=1 uv run --group docs sphinx-build -M html docs docs/_build
```
+The C++ documentation will automatically rebuild when header files change
during `sphinx-autobuild` sessions.
+
+### Build with Rust Docs
+
+Generating the Rust reference requires `cargo` to be installed:
+
+```bash
+# Install Rust toolchain if not already installed
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
+```
+
+Set `BUILD_RUST_DOCS=1` on the desired build command to enable Rust
documentation:
+
+```bash
+# Interactive build with auto-rebuild on Rust source changes
+BUILD_RUST_DOCS=1 uv run --group docs sphinx-autobuild docs docs/_build/html
--ignore docs/reference/rust/generated --watch rust
+# One-off build (clean rebuild)
+BUILD_RUST_DOCS=1 uv run --group docs sphinx-build -M html docs docs/_build
+```
+
+The Rust documentation will automatically rebuild when source files change
during `sphinx-autobuild` sessions.
+
+### Build All Documentation
+
+To build documentation with all language references enabled:
+
+```bash
+# Interactive build
+BUILD_CPP_DOCS=1 BUILD_RUST_DOCS=1 uv run --group docs sphinx-autobuild docs
docs/_build/html --ignore docs/reference/cpp/generated --ignore
docs/reference/rust/generated --watch include --watch rust
+# One-off build
+BUILD_CPP_DOCS=1 BUILD_RUST_DOCS=1 uv run --group docs sphinx-build -M html
docs docs/_build
+```
+
## Cleanup
Remove generated artifacts when they are no longer needed:
@@ -70,4 +103,5 @@ Remove generated artifacts when they are no longer needed:
rm -rf docs/_build/
rm -rf docs/reference/python/generated
rm -rf docs/reference/cpp/generated
+rm -rf docs/reference/rust/generated
```
diff --git a/docs/conf.py b/docs/conf.py
index 28f87df..82ec1fb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,6 +18,9 @@
# -*- coding: utf-8 -*-
import os
+import shutil
+import subprocess
+import sys
from pathlib import Path
import sphinx
@@ -26,7 +29,15 @@ import tomli
os.environ["TVM_FFI_BUILD_DOCS"] = "1"
build_exhale = os.environ.get("BUILD_CPP_DOCS", "0") == "1"
+build_rust_docs = os.environ.get("BUILD_RUST_DOCS", "0") == "1"
+# Auto-detect sphinx-autobuild: Check if sphinx-autobuild is in the execution
path
+is_autobuild = any("sphinx-autobuild" in str(arg) for arg in sys.argv)
+
+# -- Path constants -------------------------------------------------------
+_DOCS_DIR = Path(__file__).resolve().parent
+_RUST_DIR = _DOCS_DIR.parent / "rust"
+_RUST_OUTPUT_DIR = _DOCS_DIR / "reference" / "rust" / "generated"
# -- General configuration ------------------------------------------------
# Load version from pyproject.toml
@@ -90,6 +101,7 @@ This page contains the full API index for the C++ API.
"""
# Setup the exhale extension
+
exhale_args = {
"containmentFolder": "reference/cpp/generated",
"rootFileName": "index.rst",
@@ -151,7 +163,6 @@ autosummary_filename_map = {
"tvm_ffi.Device": "tvm_ffi.Device_class",
}
-_DOCS_DIR = Path(__file__).resolve().parent
_STUBS = {
"_stubs/cpp_index.rst": "reference/cpp/generated/index.rst",
}
@@ -167,15 +178,53 @@ def _prepare_stub_files() -> None:
dst_path.write_text(src_path.read_text(encoding="utf-8"),
encoding="utf-8")
+def _build_rust_docs() -> None:
+ """Build Rust documentation using cargo doc."""
+ if not build_rust_docs:
+ return
+
+ print("Building Rust documentation...")
+ try:
+ target_doc = _RUST_DIR / "target" / "doc"
+
+ # In auto-reload mode (sphinx-autobuild), keep incremental builds
+ # Otherwise (CI/production), do clean rebuild
+ if not is_autobuild and target_doc.exists():
+ print("Clean rebuild: removing old documentation...")
+ shutil.rmtree(target_doc)
+
+ # Generate documentation (without dependencies)
+ subprocess.run(
+ ["cargo", "doc", "--no-deps", "--workspace", "--target-dir",
"target"],
+ check=True,
+ cwd=_RUST_DIR,
+ env={**os.environ, "RUSTDOCFLAGS": "--cfg docsrs"},
+ )
+
+ # Copy generated documentation
+ if _RUST_OUTPUT_DIR.exists():
+ shutil.rmtree(_RUST_OUTPUT_DIR)
+ shutil.copytree(target_doc, _RUST_OUTPUT_DIR)
+
+ print(f"Rust documentation built successfully at {_RUST_OUTPUT_DIR}")
+ except subprocess.CalledProcessError as e:
+ print(f"Warning: Failed to build Rust documentation: {e}")
+ except FileNotFoundError:
+ print("Warning: cargo not found, skipping Rust documentation build")
+
+
def _apply_config_overrides(_: object, config: object) -> None:
"""Apply runtime configuration overrides derived from environment
variables."""
config.build_exhale = build_exhale
+ config.build_rust_docs = build_rust_docs
def setup(app: sphinx.application.Sphinx) -> None:
"""Register custom Sphinx configuration values."""
_prepare_stub_files()
+ _build_rust_docs()
app.add_config_value("build_exhale", build_exhale, "env")
+ app.add_config_value("build_rust_docs", build_rust_docs, "env")
app.connect("config-inited", _apply_config_overrides)
@@ -301,4 +350,8 @@ html_context = {
html_static_path = ["_static"]
+# Copy Rust documentation to output if enabled
+html_extra_path = ["reference/rust/generated"] if build_rust_docs else []
+
+
html_css_files = ["custom.css"]
diff --git a/docs/guides/rust_guide.md b/docs/guides/rust_guide.md
new file mode 100644
index 0000000..a1b7ac7
--- /dev/null
+++ b/docs/guides/rust_guide.md
@@ -0,0 +1,217 @@
+<!--- Licensed to the Apache Software Foundation (ASF) under one -->
+<!--- or more contributor license agreements. See the NOTICE file -->
+<!--- distributed with this work for additional information -->
+<!--- regarding copyright ownership. The ASF licenses this file -->
+<!--- to you under the Apache License, Version 2.0 (the -->
+<!--- "License"); you may not use this file except in compliance -->
+<!--- with the License. You may obtain a copy of the License at -->
+
+<!--- http://www.apache.org/licenses/LICENSE-2.0 -->
+
+<!--- Unless required by applicable law or agreed to in writing, -->
+<!--- software distributed under the License is distributed on an -->
+<!--- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -->
+<!--- KIND, either express or implied. See the License for the -->
+<!--- specific language governing permissions and limitations -->
+<!--- under the License. -->
+
+# Rust Guide
+
+```{note}
+The Rust support is currently in an experimental stage.
+```
+
+This guide demonstrates how to use TVM FFI from Rust applications.
+
+## Installation
+
+### Prerequisites
+
+The Rust support depends on `libtvm_ffi`. First, install the `tvm-ffi` Python
package:
+
+```bash
+pip install -v -e .
+```
+
+Confirm that `tvm-ffi-config` is available:
+
+```bash
+tvm-ffi-config --libdir
+```
+
+### Adding to Your Project
+
+Add to your `Cargo.toml`:
+
+```toml
+[dependencies]
+tvm-ffi = { path = "path/to/tvm-ffi/rust/tvm-ffi" }
+```
+
+For published versions (when available):
+
+```toml
+[dependencies]
+tvm-ffi = "0.1.0-alpha.0"
+```
+
+### Environment Setup
+
+Set the library path so `libtvm_ffi` can be found at runtime:
+
+```bash
+export LD_LIBRARY_PATH=$(tvm-ffi-config --libdir):$LD_LIBRARY_PATH
+```
+
+## Basic Usage
+
+### Loading a Module
+
+Load a compiled TVM FFI module and call its functions:
+
+```rust
+use tvm_ffi::{Module, Result};
+
+fn main() -> Result<()> {
+ // Load compiled module
+ let module = Module::load_from_file("build/add_one_cpu.so")?;
+
+ // Get function by name
+ let add_fn = module.get_function("add_one_cpu")?;
+
+ Ok(())
+}
+```
+
+### Working with Tensors
+
+Create and manipulate tensors:
+
+```rust
+use tvm_ffi::Tensor;
+
+// Create a tensor from a slice
+let data = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
+let tensor = Tensor::from_slice(&data, &[2, 3])?;
+```
+
+### Calling Functions
+
+Call functions with tensors:
+
+```rust
+use tvm_ffi::{Module, Tensor, Result};
+
+fn run_example() -> Result<()> {
+ let module = Module::load_from_file("build/add_one_cpu.so")?;
+ let func = module.get_function("add_one_cpu")?;
+
+ // Create input and output tensors
+ let input = Tensor::from_slice(&[1.0f32, 2.0, 3.0, 4.0], &[4])?;
+ let output = Tensor::from_slice(&[0.0f32; 4], &[4])?;
+
+ // Call function
+ func.call_tuple((&input, &output))?;
+
+ Ok(())
+}
+```
+
+## Advanced Topics
+
+### Global Functions
+
+Register and access global functions:
+
+```rust
+use tvm_ffi::Function;
+
+// Get global function
+let func = Function::get_global("my_function")?;
+
+// Register a new global function
+let my_func = Function::from_packed(|args: &[AnyView]| -> Result<Any> {
+ // Function implementation
+ Ok(Any::default())
+});
+Function::register_global("my_custom_func", my_func)?;
+```
+
+### Type-Erased Functions
+
+Create functions from Rust closures:
+
+```rust
+use tvm_ffi::{Function, Any, AnyView, Result};
+
+// From packed closure
+let func = Function::from_packed(|args: &[AnyView]| -> Result<Any> {
+ // Process args and return result
+ Ok(Any::default())
+});
+
+// From typed closure
+let typed_func = Function::from_typed(|x: i64, y: i64| -> Result<i64> {
+ Ok(x + y)
+});
+```
+
+### Error Handling
+
+TVM FFI uses standard Rust `Result` types:
+
+```rust
+use tvm_ffi::{Error, Module, Result, VALUE_ERROR};
+
+fn may_fail(value: i32) -> Result<()> {
+ // Operations that may fail
+ let module = Module::load_from_file("path.so")?;
+
+ // Custom errors
+ if value < 0 {
+ return Err(Error::new(
+ VALUE_ERROR,
+ "Value must be non-negative",
+ ""
+ ));
+ }
+
+ Ok(())
+}
+```
+
+## Examples
+
+The repository includes a complete example in
`rust/tvm-ffi/examples/load_library.rs`.
+
+Run it with:
+
+```bash
+cd rust
+cargo run --example load_library --features example
+```
+
+## Building the Workspace
+
+Build the entire Rust workspace:
+
+```bash
+cd rust
+cargo build
+```
+
+Run tests:
+
+```bash
+cargo test
+```
+
+## API Reference
+
+For detailed API documentation, see the [Rust API
Reference](../reference/rust/index.rst).
+
+## Related Resources
+
+- [Quick Start Guide](../get_started/quickstart.rst) - General TVM FFI
introduction
+- [C++ Guide](cpp_guide.md) - C++ API usage
+- [Python Guide](python_guide.md) - Python API usage
diff --git a/docs/index.rst b/docs/index.rst
index 5e8fd0c..f885799 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -48,6 +48,7 @@ Table of Contents
guides/python_packaging.md
guides/cpp_guide.md
guides/python_guide.md
+ guides/rust_guide.md
guides/stable_c_abi.md
guides/compiler_integration.md
guides/build_from_source.md
@@ -66,3 +67,4 @@ Table of Contents
reference/python/index.rst
reference/cpp/index.rst
+ reference/rust/index.rst
diff --git a/docs/reference/rust/index.rst b/docs/reference/rust/index.rst
new file mode 100644
index 0000000..12a71be
--- /dev/null
+++ b/docs/reference/rust/index.rst
@@ -0,0 +1,40 @@
+.. Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+.. http://www.apache.org/licenses/LICENSE-2.0
+
+.. Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+Rust API
+========
+
+This page contains the API reference for the Rust API. The tvm-ffi project
provides
+Rust bindings that allow you to use the TVM FFI from Rust programs.
+
+Crates
+------
+
+The Rust API is organized into three crates:
+
+.. list-table::
+ :header-rows: 1
+ :widths: 30 70
+
+ * - Crate
+ - Description
+ * - 📦 `tvm-ffi <generated/tvm_ffi/index.html>`_
+ - High-level Rust bindings with safe abstractions for FFI objects,
functions, tensors, and containers.
+ * - 📦 `tvm-ffi-sys <generated/tvm_ffi_sys/index.html>`_
+ - Low-level unsafe bindings to the C API.
+ * - 📦 `tvm-ffi-macros <generated/tvm_ffi_macros/index.html>`_
+ - Procedural macros for deriving Object and ObjectRef traits.
diff --git a/rust/tvm-ffi-sys/build.rs b/rust/tvm-ffi-sys/build.rs
index 7cd7f24..1f2e64a 100644
--- a/rust/tvm-ffi-sys/build.rs
+++ b/rust/tvm-ffi-sys/build.rs
@@ -47,15 +47,37 @@ fn update_ld_library_path(lib_dir: &str) {
}
fn main() {
+ // When building documentation, we may not need actual linking
+ // Check if this is a rustdoc build
+ let is_rustdoc = env::var("RUSTDOC").is_ok() ||
env::var("CARGO_CFG_DOC").is_ok();
+
// Run `mylib-config --libdir` to get the library path
- let config_output = Command::new("tvm-ffi-config")
- .arg("--libdir")
- .output()
- .expect("Failed to run tvm-ffi-config");
- let lib_dir = String::from_utf8(config_output.stdout)
- .expect("Invalid UTF-8 output from tvm-ffi-config")
- .trim()
- .to_string();
+ let config_result =
Command::new("tvm-ffi-config").arg("--libdir").output();
+
+ let lib_dir = match config_result {
+ Ok(output) if output.status.success() =>
String::from_utf8(output.stdout)
+ .unwrap_or_default()
+ .trim()
+ .to_string(),
+ _ if is_rustdoc => {
+ // For rustdoc builds, we can proceed without the library
+ eprintln!("Warning: tvm-ffi-config not available, skipping for
documentation build");
+ return;
+ }
+ _ => {
+ panic!("Failed to run tvm-ffi-config. Make sure tvm-ffi is
installed.");
+ }
+ };
+
+ if lib_dir.is_empty() {
+ if is_rustdoc {
+ eprintln!(
+ "Warning: Empty lib_dir from tvm-ffi-config, skipping for
documentation build"
+ );
+ return;
+ }
+ panic!("tvm-ffi-config returned empty library path");
+ }
// add the library directory to the linker search path
println!("cargo:rustc-link-search=native={}", lib_dir);