From 1075538fb34add68c62f07c035cc64f0ef2a99bc Mon Sep 17 00:00:00 2001 From: David Holtz Date: Fri, 2 Oct 2026 13:13:16 +0000 Subject: [PATCH 1/4] feat: add rust cpu to v6 support --- docs/source/builder/writing-kernels.md | 9 +- examples/kernels/flake.nix | 20 ++++ examples/kernels/relu-rust/CARD.md | 65 +++++++++++ examples/kernels/relu-rust/Cargo.lock | 107 ++++++++++++++++++ examples/kernels/relu-rust/Cargo.toml | 3 + examples/kernels/relu-rust/build.toml | 17 +++ examples/kernels/relu-rust/flake.nix | 17 +++ examples/kernels/relu-rust/relu-rs/Cargo.toml | 12 ++ examples/kernels/relu-rust/relu-rs/src/lib.rs | 28 +++++ examples/kernels/relu-rust/tests/__init__.py | 1 + examples/kernels/relu-rust/tests/test_relu.py | 31 +++++ .../tvm-ffi-ext/relu_rust/__init__.py | 19 ++++ kernel-builder/src/pyproject/kernel.rs | 25 ++++ .../templates/kernel-component/rust-cpu.cmake | 6 + .../src/pyproject/templates/rust.cmake | 86 ++++++++++++++ .../pyproject/templates/tvm_ffi/binding.cmake | 2 + .../templates/tvm_ffi/preamble.cmake | 5 + .../templates/tvm_ffi/tvm-ffi-extension.cmake | 4 + kernel-builder/src/pyproject/tvm_ffi/mod.rs | 2 + kernels-common/src/config/compat.rs | 2 +- kernels-common/src/config/mod.rs | 101 ++++++++++++++++- kernels-common/src/config/v6.rs | 54 +++++++-- nix-builder/lib/build.nix | 4 + nix-builder/lib/extension/tvm-ffi/arch.nix | 19 ++++ nix-builder/lib/kernel-config.nix | 3 +- nix-builder/lib/source-set.nix | 6 +- 26 files changed, 633 insertions(+), 15 deletions(-) create mode 100644 examples/kernels/relu-rust/CARD.md create mode 100644 examples/kernels/relu-rust/Cargo.lock create mode 100644 examples/kernels/relu-rust/Cargo.toml create mode 100644 examples/kernels/relu-rust/build.toml create mode 100644 examples/kernels/relu-rust/flake.nix create mode 100644 examples/kernels/relu-rust/relu-rs/Cargo.toml create mode 100644 examples/kernels/relu-rust/relu-rs/src/lib.rs create mode 100644 examples/kernels/relu-rust/tests/__init__.py create mode 100644 examples/kernels/relu-rust/tests/test_relu.py create mode 100644 examples/kernels/relu-rust/tvm-ffi-ext/relu_rust/__init__.py create mode 100644 kernel-builder/src/pyproject/templates/kernel-component/rust-cpu.cmake create mode 100644 kernel-builder/src/pyproject/templates/rust.cmake diff --git a/docs/source/builder/writing-kernels.md b/docs/source/builder/writing-kernels.md index 39cfb70f7..1f782bf69 100644 --- a/docs/source/builder/writing-kernels.md +++ b/docs/source/builder/writing-kernels.md @@ -318,7 +318,7 @@ The following options can be set for a kernel: - `language` (required): the language and compute backend of the kernel, as `-`. The currently supported values are `cpp-cpu`, - `cpp-cuda`, `cpp-metal`, `cpp-rocm`, and `cpp-xpu`. + `cpp-cuda`, `cpp-metal`, `cpp-rocm`, `cpp-xpu`, and `rust-cpu`. **The `cpu` backend is currently experimental and might still change.** - `depends` (required): a list of dependencies. The supported dependencies are listed in [`cpp-deps.nix`](https://github.com/huggingface/kernels/blob/main/builder/lib/cpp-deps.nix). @@ -357,6 +357,13 @@ are available: - `cxx-flags`: a list of additional flags to be passed to the C++ compiler. +#### rust-cpu + +Rust kernels are built with Cargo and require the `tvm-ffi` framework. +`include` and `cxx-flags` do not apply to them. + +- `cargo-manifest` (required): path of the crate's `Cargo.toml`. + ## Torch bindings ### Defining bindings diff --git a/examples/kernels/flake.nix b/examples/kernels/flake.nix index 4d48608e6..0232bd91d 100644 --- a/examples/kernels/flake.nix +++ b/examples/kernels/flake.nix @@ -355,6 +355,26 @@ # CPU kernels to build in CI. ciCpuKernels = [ + { + name = "relu-rust-kernel"; + path = ./relu-rust; + drv = + sys: out: + let + variant = "tvm-ffi${tvmFfiVersion}-cpu-${sys}"; + extension = out.packages.${sys}.redistributable.${variant}; + ciTest = out.packages.${sys}.ciTests.${variant}; + kernelPkgs = out.packages.${sys}.pkgs.${variant}; + in + kernelPkgs.runCommand "relu-rust-kernel-test" + { + nativeBuildInputs = [ ciTest ]; + } + '' + ${ciTest}/bin/ci-test + ln -s ${extension} $out + ''; + } { # This test only requires a CPU, so let's run the test directly during the build. name = "symbol-conflicts-pytest"; diff --git a/examples/kernels/relu-rust/CARD.md b/examples/kernels/relu-rust/CARD.md new file mode 100644 index 000000000..2868c79f5 --- /dev/null +++ b/examples/kernels/relu-rust/CARD.md @@ -0,0 +1,65 @@ +--- +library_name: kernels +{% if license %}license: {{ license }} +{% endif %}--- + +This is the repository card of {{ repo_id }} that has been pushed on the Hub. It was built to be used with the [`kernels` library](https://github.com/huggingface/kernels). This card was automatically generated. + +## How to use +{% if functions %} + +```python +# make sure `kernels` is installed: `pip install -U kernels` +from kernels import get_kernel + +# If the org / user isn't a trusted publisher, pass `trust_remote_code=True` to the +# `get_kernel` call. You can find whether this kernel is from a trusted publisher +# by going to the kernel's Hub page and finding the "Trusted publisher" status at +# the top of the page. +kernel_module = get_kernel("{{ repo_id }}", version={{ version }}) +{{ functions[0] }} = kernel_module.{{ functions[0] }} + +{{ functions[0] }}(...) +``` +{% else %} + +Usage example not available. +{% endif %} + +## Available functions +{% if functions %} +{% for func in functions %} +- `{{ func }}` +{% endfor %} +{% else %} + +Function list not available. +{% endif %} +{% if layers %} + +## Available layers +{% for layer in layers %} +- `{{ layer }}` +{% endfor %} +{% endif %} + +## Benchmarks +{% if has_benchmark %} + +Benchmarking script is available for this kernel. Run `kernels benchmark {{ repo_id }} --version {{ version }}`. +{% else %} + +No benchmark available yet. +{% endif %} +{% if upstream %} + +## Upstream + +The original source code for this kernel comes from {{ upstream }}. +{% endif %} +{% if source %} + +## Source + +The kernel-builder formatted source for this kernel is available at {{ source }}. +{% endif %} diff --git a/examples/kernels/relu-rust/Cargo.lock b/examples/kernels/relu-rust/Cargo.lock new file mode 100644 index 000000000..af709f9b9 --- /dev/null +++ b/examples/kernels/relu-rust/Cargo.lock @@ -0,0 +1,107 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "relu-rs" +version = "0.1.0" +dependencies = [ + "tvm-ffi", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tvm-ffi" +version = "0.1.0-alpha.0" +source = "git+https://github.com/apache/tvm-ffi.git?rev=2af558e255ff2f398095835ae18e6457635b0262#2af558e255ff2f398095835ae18e6457635b0262" +dependencies = [ + "paste", + "tvm-ffi-macros", + "tvm-ffi-sys", +] + +[[package]] +name = "tvm-ffi-macros" +version = "0.1.0-alpha.0" +source = "git+https://github.com/apache/tvm-ffi.git?rev=2af558e255ff2f398095835ae18e6457635b0262#2af558e255ff2f398095835ae18e6457635b0262" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tvm-ffi-sys" +version = "0.1.0-alpha.0" +source = "git+https://github.com/apache/tvm-ffi.git?rev=2af558e255ff2f398095835ae18e6457635b0262#2af558e255ff2f398095835ae18e6457635b0262" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" diff --git a/examples/kernels/relu-rust/Cargo.toml b/examples/kernels/relu-rust/Cargo.toml new file mode 100644 index 000000000..f79b1ef2b --- /dev/null +++ b/examples/kernels/relu-rust/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["relu-rs"] +resolver = "2" diff --git a/examples/kernels/relu-rust/build.toml b/examples/kernels/relu-rust/build.toml new file mode 100644 index 000000000..7eab81924 --- /dev/null +++ b/examples/kernels/relu-rust/build.toml @@ -0,0 +1,17 @@ +[general] +name = "relu-rust" +version = 1 +edition = 6 +license = "Apache-2.0" +backends = ["cpu"] + +[general.hub] +repo-id = "kernels-test/relu-rust" + +[tvm-ffi] + +[kernel.relu_rust] +language = "rust-cpu" +cargo-manifest = "relu-rs/Cargo.toml" +depends = [] +src = ["relu-rs/src/lib.rs", "Cargo.toml"] diff --git a/examples/kernels/relu-rust/flake.nix b/examples/kernels/relu-rust/flake.nix new file mode 100644 index 000000000..b8ec0be90 --- /dev/null +++ b/examples/kernels/relu-rust/flake.nix @@ -0,0 +1,17 @@ +{ + description = "Flake for a ReLU kernel written in Rust"; + + inputs = { + kernel-builder.url = "path:../../.."; + }; + + outputs = + { + self, + kernel-builder, + }: + kernel-builder.lib.genKernelFlakeOutputs { + inherit self; + path = ./.; + }; +} diff --git a/examples/kernels/relu-rust/relu-rs/Cargo.toml b/examples/kernels/relu-rust/relu-rs/Cargo.toml new file mode 100644 index 000000000..2717eac90 --- /dev/null +++ b/examples/kernels/relu-rust/relu-rs/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "relu-rs" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +publish = false + +[lib] +name = "relu_rust" + +[dependencies] +tvm-ffi = { git = "https://github.com/apache/tvm-ffi.git", rev = "2af558e255ff2f398095835ae18e6457635b0262" } diff --git a/examples/kernels/relu-rust/relu-rs/src/lib.rs b/examples/kernels/relu-rust/relu-rs/src/lib.rs new file mode 100644 index 000000000..b27935c3c --- /dev/null +++ b/examples/kernels/relu-rust/relu-rs/src/lib.rs @@ -0,0 +1,28 @@ +use tvm_ffi::{Error, Result, Tensor, VALUE_ERROR}; + +fn relu_rust(x: Tensor, out: Tensor) -> Result<()> { + let x_data = x.data_as_slice::()?; + let out_data = out.data_as_slice_mut::()?; + + // `zip` would otherwise stop at the shorter slice, leaving the tail of a + // larger `out` holding whatever `empty_like` allocated. + if x_data.len() != out_data.len() { + return Err(Error::new( + VALUE_ERROR, + &format!( + "input and output must have the same number of elements, got {} and {}", + x_data.len(), + out_data.len() + ), + "", + )); + } + + for (out_elem, &x_elem) in out_data.iter_mut().zip(x_data.iter()) { + *out_elem = x_elem.max(0.0); + } + + Ok(()) +} + +tvm_ffi::tvm_ffi_dll_export_typed_func!(relu_rust, relu_rust); diff --git a/examples/kernels/relu-rust/tests/__init__.py b/examples/kernels/relu-rust/tests/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/kernels/relu-rust/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/examples/kernels/relu-rust/tests/test_relu.py b/examples/kernels/relu-rust/tests/test_relu.py new file mode 100644 index 000000000..b6beb742b --- /dev/null +++ b/examples/kernels/relu-rust/tests/test_relu.py @@ -0,0 +1,31 @@ +import ctypes +import sys + +import kernels +import pytest +import torch +import torch.nn.functional as F + +relu_rust = kernels.get_kernel("kernels-test/relu-rust", version=1) + + +@pytest.mark.kernels_ci +def test_relu(): + x = torch.randn(1024, 1024, dtype=torch.float32, device="cpu") + torch.testing.assert_close(F.relu(x), relu_rust.relu(x, torch.empty_like(x))) + + +@pytest.mark.kernels_ci +def test_rejects_mismatched_output(): + x = torch.randn(16, dtype=torch.float32, device="cpu") + with pytest.raises(Exception, match="same number of elements"): + relu_rust.relu(x, torch.empty(32, dtype=torch.float32, device="cpu")) + + +@pytest.mark.kernels_ci +@pytest.mark.skipif(sys.platform != "linux", reason="ELF symbol isolation") +def test_rust_symbols_remain_local(): + # get_kernel has already loaded the extension. Its Rust export must not + # become visible to unrelated extensions through the global symbol scope. + with pytest.raises(AttributeError): + getattr(ctypes.CDLL(None), "__tvm_ffi_relu_rust") diff --git a/examples/kernels/relu-rust/tvm-ffi-ext/relu_rust/__init__.py b/examples/kernels/relu-rust/tvm-ffi-ext/relu_rust/__init__.py new file mode 100644 index 000000000..69f606da4 --- /dev/null +++ b/examples/kernels/relu-rust/tvm-ffi-ext/relu_rust/__init__.py @@ -0,0 +1,19 @@ +import tvm_ffi + +from ._ops import ops + + +def relu(x, out): + x_t = tvm_ffi.from_dlpack(x) + out_t = tvm_ffi.from_dlpack(out) + + device = x_t.device + if device.type == "cpu": + ops.relu_rust(x_t, out_t) + else: + raise NotImplementedError(f"Unsupported device type: {device.type}") + + return out + + +__all__ = ["relu"] diff --git a/kernel-builder/src/pyproject/kernel.rs b/kernel-builder/src/pyproject/kernel.rs index d8000ebf4..43fa036b2 100644 --- a/kernel-builder/src/pyproject/kernel.rs +++ b/kernel-builder/src/pyproject/kernel.rs @@ -37,6 +37,9 @@ fn render_kernel_component( Kernel::CppCpu { .. } => { render_kernel_component_cpu(env, kernel_name, kernel, sources, write)? } + Kernel::RustCpu { cargo_manifest, .. } => { + render_kernel_component_rust(env, kernel_name, cargo_manifest, write)? + } Kernel::CppCuda { .. } => { render_kernel_component_cuda(env, kernel_name, kernel, sources, write)? } @@ -54,6 +57,28 @@ fn render_kernel_component( Ok(()) } +fn render_kernel_component_rust( + env: &Environment, + kernel_name: &str, + cargo_manifest: &str, + write: &mut impl Write, +) -> Result<()> { + env.get_template("kernel-component/rust-cpu.cmake") + .wrap_err("Cannot get kernel template")? + .render_captured_to( + context! { + manifest_path => cargo_manifest, + name => kernel_name, + }, + &mut *write, + ) + .wrap_err("Cannot render kernel template")?; + + write.write_all(b"\n")?; + + Ok(()) +} + fn render_kernel_component_cpu( env: &Environment, kernel_name: &str, diff --git a/kernel-builder/src/pyproject/templates/kernel-component/rust-cpu.cmake b/kernel-builder/src/pyproject/templates/kernel-component/rust-cpu.cmake new file mode 100644 index 000000000..cc8eafbb3 --- /dev/null +++ b/kernel-builder/src/pyproject/templates/kernel-component/rust-cpu.cmake @@ -0,0 +1,6 @@ +if(GPU_LANG STREQUAL "CPU") +rust_kernel_component(RUST_KERNEL_LIBS RUST_KERNEL_TARGETS + NAME {{ name }} + MANIFEST_PATH "{{ manifest_path }}" +) +endif() diff --git a/kernel-builder/src/pyproject/templates/rust.cmake b/kernel-builder/src/pyproject/templates/rust.cmake new file mode 100644 index 000000000..727fff427 --- /dev/null +++ b/kernel-builder/src/pyproject/templates/rust.cmake @@ -0,0 +1,86 @@ +function(rust_kernel_component LIBS_VAR TARGETS_VAR) + cmake_parse_arguments(KERNEL "" "NAME;MANIFEST_PATH" "" ${ARGN}) + + if(NOT KERNEL_NAME OR NOT KERNEL_MANIFEST_PATH) + message(FATAL_ERROR "rust_kernel_component: NAME and MANIFEST_PATH are required") + endif() + + if(NOT CARGO_EXECUTABLE) + message(FATAL_ERROR "Kernel component `${KERNEL_NAME}` is written in Rust, " + "but `cargo` was not found. Install a Rust toolchain or set CARGO_EXECUTABLE.") + endif() + + string(REPLACE "-" "_" _LIB_NAME ${KERNEL_NAME}) + + set(_CARGO_TARGET_DIR ${CMAKE_BINARY_DIR}/cargo/${KERNEL_NAME}) + set(_STATICLIB ${_CARGO_TARGET_DIR}/release/${CMAKE_STATIC_LIBRARY_PREFIX}${_LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}) + + # tvm-ffi-sys's build script shells out to `tvm-ffi-config`, a console script + # the apache-tvm-ffi wheel installs beside the interpreter. + get_filename_component(_PYTHON_BIN_DIR ${Python_EXECUTABLE} DIRECTORY) + + add_custom_target(${KERNEL_NAME}_cargo_build ALL + COMMAND ${CMAKE_COMMAND} -E env "PATH=${_PYTHON_BIN_DIR}:$ENV{PATH}" + ${CARGO_EXECUTABLE} rustc --release --locked --lib --crate-type staticlib + --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/${KERNEL_MANIFEST_PATH} + --target-dir ${_CARGO_TARGET_DIR} + BYPRODUCTS ${_STATICLIB} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Building Rust kernel ${KERNEL_NAME} with cargo" + VERBATIM + ) + + add_library(${KERNEL_NAME}_rust STATIC IMPORTED GLOBAL) + set_target_properties(${KERNEL_NAME}_rust PROPERTIES IMPORTED_LOCATION ${_STATICLIB}) + + set(${LIBS_VAR} ${${LIBS_VAR}} ${KERNEL_NAME}_rust PARENT_SCOPE) + set(${TARGETS_VAR} ${${TARGETS_VAR}} ${KERNEL_NAME}_cargo_build PARENT_SCOPE) +endfunction() + +# `add_library(SHARED)` errors on an empty source list, and a Rust-only +# extension has none: the crate exports the tvm-ffi entry points itself. +function(rust_extension_sources SRC_VAR) + if(${SRC_VAR}) + return() + endif() + if(NOT RUST_KERNEL_LIBS) + message(FATAL_ERROR "No sources for the ${BACKEND} extension. Set " + "`[tvm-ffi].src` or give this backend a kernel component.") + endif() + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/_ops_stub.cpp "\n") + set(${SRC_VAR} ${${SRC_VAR}} ${CMAKE_CURRENT_BINARY_DIR}/_ops_stub.cpp PARENT_SCOPE) +endfunction() + +# Whole-archive linking publishes the crate's bundled `std` at default +# visibility, so export only the names tvm-ffi resolves at load time. +function(_restrict_rust_exports TARGET) + set(_EXPORTS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-rust-exports) + if(APPLE) + file(WRITE ${_EXPORTS} "___tvm_ffi_*\n") + set(_FLAG "-exported_symbols_list,${_EXPORTS}") + elseif(UNIX) + file(WRITE ${_EXPORTS} "{ global: __tvm_ffi_*; local: *; };\n") + set(_FLAG "--version-script=${_EXPORTS}") + else() + message(FATAL_ERROR "Rust kernels are not supported on this platform " + "(cannot restrict exported symbols to the tvm-ffi entry points)") + endif() + + target_link_options(${TARGET} PRIVATE "LINKER:${_FLAG}") + set_property(TARGET ${TARGET} APPEND PROPERTY LINK_DEPENDS ${_EXPORTS}) +endfunction() + +function(target_link_rust_kernels TARGET) + if(NOT RUST_KERNEL_LIBS) + return() + endif() + + find_package(Threads REQUIRED) + add_dependencies(${TARGET} ${RUST_KERNEL_TARGETS}) + target_link_libraries(${TARGET} PRIVATE + "$" + Threads::Threads + ${CMAKE_DL_LIBS}) + _restrict_rust_exports(${TARGET}) +endfunction() diff --git a/kernel-builder/src/pyproject/templates/tvm_ffi/binding.cmake b/kernel-builder/src/pyproject/templates/tvm_ffi/binding.cmake index 8c89d5fb1..a442310bd 100644 --- a/kernel-builder/src/pyproject/templates/tvm_ffi/binding.cmake +++ b/kernel-builder/src/pyproject/templates/tvm_ffi/binding.cmake @@ -1,3 +1,4 @@ +{% if src %} set(TVM_FFI_{{name}}_SRC {{ src|join(' ') }} ) @@ -18,3 +19,4 @@ set_property( {% endif %} list(APPEND SRC {{'"${TVM_FFI_' + name + '_SRC}"'}}) +{% endif %} diff --git a/kernel-builder/src/pyproject/templates/tvm_ffi/preamble.cmake b/kernel-builder/src/pyproject/templates/tvm_ffi/preamble.cmake index 294d791b4..a0443f9db 100644 --- a/kernel-builder/src/pyproject/templates/tvm_ffi/preamble.cmake +++ b/kernel-builder/src/pyproject/templates/tvm_ffi/preamble.cmake @@ -25,6 +25,11 @@ message(STATUS "FetchContent base directory: ${FETCHCONTENT_BASE_DIR}") include(CheckCXXCompilerFlag) include(${CMAKE_CURRENT_LIST_DIR}/cmake/utils.cmake) include(${CMAKE_CURRENT_LIST_DIR}/cmake/kernel.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cmake/rust.cmake) + +# Not REQUIRED: only kernels with Rust components need cargo, and +# rust_kernel_component errors out if it is missing. +find_program(CARGO_EXECUTABLE cargo) if(NOT DEFINED GPU_LANG) if(ICX_COMPILER OR ICPX_COMPILER) diff --git a/kernel-builder/src/pyproject/templates/tvm_ffi/tvm-ffi-extension.cmake b/kernel-builder/src/pyproject/templates/tvm_ffi/tvm-ffi-extension.cmake index 79a1600a6..0fe3e32f1 100644 --- a/kernel-builder/src/pyproject/templates/tvm_ffi/tvm-ffi-extension.cmake +++ b/kernel-builder/src/pyproject/templates/tvm_ffi/tvm-ffi-extension.cmake @@ -1,6 +1,8 @@ # Avoid 'lib' prefix for the extension. set(CMAKE_SHARED_LIBRARY_PREFIX "") +rust_extension_sources(SRC) + add_library(${OPS_NAME} SHARED ${SRC}) target_compile_definitions(${OPS_NAME} PRIVATE "-DTVM_FFI_EXTENSION_NAME=${OPS_NAME}") @@ -15,6 +17,8 @@ if(CXX_HAS_NO_GNU_UNIQUE) target_compile_options(${OPS_NAME} PRIVATE $<$:-fno-gnu-unique>) endif() +target_link_rust_kernels(${OPS_NAME}) + if(GPU_LANG STREQUAL "SYCL") target_link_options(${OPS_NAME} PRIVATE ${sycl_link_flags}) target_link_libraries(${OPS_NAME} PRIVATE dnnl) diff --git a/kernel-builder/src/pyproject/tvm_ffi/mod.rs b/kernel-builder/src/pyproject/tvm_ffi/mod.rs index a7c6ad578..7f40d150c 100644 --- a/kernel-builder/src/pyproject/tvm_ffi/mod.rs +++ b/kernel-builder/src/pyproject/tvm_ffi/mod.rs @@ -16,6 +16,7 @@ use crate::pyproject::FileSet; static BUILD_VARIANTS_UTILS: &str = include_str!("../templates/tvm_ffi/build-variants.cmake"); static CMAKE_KERNEL: &str = include_str!("../templates/kernel.cmake"); +static CMAKE_RUST: &str = include_str!("../templates/rust.cmake"); static CMAKE_UTILS: &str = include_str!("../templates/utils.cmake"); static OPS_PY_IN: &str = include_str!("../templates/tvm_ffi/_ops.py.in"); static DETECT_CUDA_CAPABILITY_PY: &str = @@ -24,6 +25,7 @@ static DETECT_CUDA_CAPABILITY_PY: &str = fn write_cmake_helpers(file_set: &mut FileSet) { write_cmake_file(file_set, "utils.cmake", CMAKE_UTILS.as_bytes()); write_cmake_file(file_set, "kernel.cmake", CMAKE_KERNEL.as_bytes()); + write_cmake_file(file_set, "rust.cmake", CMAKE_RUST.as_bytes()); write_cmake_file( file_set, "build-variants.cmake", diff --git a/kernels-common/src/config/compat.rs b/kernels-common/src/config/compat.rs index b4d6dcb18..3fb6a8754 100644 --- a/kernels-common/src/config/compat.rs +++ b/kernels-common/src/config/compat.rs @@ -85,7 +85,7 @@ impl TryFrom for Build { BuildCompat::V3(v3_build) => v3_build.try_into(), BuildCompat::V4(v4_build) => Ok(v4_build.into()), BuildCompat::V5(v5_build) => Ok(v5_build.into()), - BuildCompat::V6(v6_build) => Ok(v6_build.into()), + BuildCompat::V6(v6_build) => v6_build.try_into(), } } } diff --git a/kernels-common/src/config/mod.rs b/kernels-common/src/config/mod.rs index 74a8a832c..fb1de026f 100644 --- a/kernels-common/src/config/mod.rs +++ b/kernels-common/src/config/mod.rs @@ -54,7 +54,7 @@ pub struct Build { impl Build { pub fn open(kernel_dir: impl AsRef) -> Result { let build_compat = parse::parse_and_validate(kernel_dir)?; - Ok(build_compat.into()) + Ok(build_compat.try_into()?) } pub fn is_noarch(&self) -> bool { @@ -350,6 +350,12 @@ pub enum Kernel { include: Option>, src: Vec, }, + RustCpu { + /// Path of the crate's `Cargo.toml`, relative to the kernel directory. + cargo_manifest: String, + depends: Vec, + src: Vec, + }, CppCuda { cuda_capabilities: Option>, cuda_flags: Option>, @@ -390,6 +396,7 @@ impl Kernel { | Kernel::CppMetal { cxx_flags, .. } | Kernel::CppRocm { cxx_flags, .. } | Kernel::CppXpu { cxx_flags, .. } => cxx_flags.as_deref(), + Kernel::RustCpu { .. } => None, } } @@ -400,6 +407,7 @@ impl Kernel { | Kernel::CppMetal { include, .. } | Kernel::CppRocm { include, .. } | Kernel::CppXpu { include, .. } => include.as_deref(), + Kernel::RustCpu { .. } => None, } } @@ -412,7 +420,7 @@ impl Kernel { pub fn backend(&self) -> Backend { match self { - Kernel::CppCpu { .. } => Backend::Cpu, + Kernel::CppCpu { .. } | Kernel::RustCpu { .. } => Backend::Cpu, Kernel::CppCuda { .. } => Backend::Cuda, Kernel::CppMetal { .. } => Backend::Metal, Kernel::CppRocm { .. } => Backend::Rocm, @@ -427,12 +435,14 @@ impl Kernel { | Kernel::CppMetal { .. } | Kernel::CppRocm { .. } | Kernel::CppXpu { .. } => Language::Cpp, + Kernel::RustCpu { .. } => Language::Rust, } } pub fn depends(&self) -> &[Dependency] { match self { Kernel::CppCpu { depends, .. } + | Kernel::RustCpu { depends, .. } | Kernel::CppCuda { depends, .. } | Kernel::CppMetal { depends, .. } | Kernel::CppRocm { depends, .. } @@ -443,6 +453,7 @@ impl Kernel { pub fn src(&self) -> &[String] { match self { Kernel::CppCpu { src, .. } + | Kernel::RustCpu { src, .. } | Kernel::CppCuda { src, .. } | Kernel::CppMetal { src, .. } | Kernel::CppRocm { src, .. } @@ -456,6 +467,7 @@ impl Kernel { #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub enum Language { Cpp, + Rust, } #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] @@ -536,6 +548,8 @@ impl FromStr for Backend { pub enum ConfigError { #[error("Cannot migrate configuration: {reason:?}")] Migration { reason: String }, + #[error("Kernel `{name}`: {reason}")] + InvalidKernel { name: String, reason: String }, } #[cfg(test)] @@ -632,7 +646,7 @@ mod tests { let parsed: v6::Build = toml::from_str(config).unwrap(); let serialized = toml::to_string(&parsed).unwrap(); - let build = Build::from(toml::from_str::(&serialized).unwrap()); + let build = Build::try_from(toml::from_str::(&serialized).unwrap()).unwrap(); assert_eq!(build.kernels["relu"].backend(), Backend::Cuda); assert_eq!(build.kernels["relu"].language(), Language::Cpp); @@ -694,4 +708,85 @@ mod tests { let err = toml::from_str::(config).unwrap_err().to_string(); assert!(err.contains("unknown field `cuda-flags`"), "{err}"); } + + #[test] + fn v6_rust_cpu_round_trip() { + let config = r#" + [general] + name = "rust-cpu" + version = 1 + edition = 6 + license = "Apache-2.0" + backends = ["cpu"] + + [tvm-ffi] + + [kernel.cpu_kernel] + language = "rust-cpu" + cargo-manifest = "cpu/Cargo.toml" + depends = [] + src = ["cpu/src/lib.rs"] + "#; + + let parsed: v6::Build = toml::from_str(config).unwrap(); + let serialized = toml::to_string(&parsed).unwrap(); + let build = Build::try_from(toml::from_str::(&serialized).unwrap()).unwrap(); + + assert_eq!(build.kernels["cpu_kernel"].language(), Language::Rust); + } + + #[test] + fn v6_rust_kernel_rejects_invalid_config() { + let cases = [ + ( + "[tvm-ffi]", + "rust-cpu", + r#"cxx-flags = ["-O3"]"#, + "unknown field `cxx-flags`", + ), + ( + "[tvm-ffi]", + "rust-cpu", + r#"include = ["."]"#, + "unknown field `include`", + ), + ( + "[torch]\nsrc = []", + "rust-cpu", + "", + "require a `[tvm-ffi]` framework", + ), + ]; + + for (framework, language, extra, expected) in cases { + let config = format!( + r#" + [general] + name = "rust-cpu" + version = 1 + edition = 6 + license = "Apache-2.0" + backends = ["cpu"] + + {framework} + + [kernel.cpu_kernel] + language = "{language}" + cargo-manifest = "Cargo.toml" + depends = [] + src = [] + {extra} + "# + ); + + let err = match toml::from_str::(&config) { + Ok(build) => Build::try_from(build) + .err() + .expect("conversion should fail") + .to_string(), + Err(err) => err.to_string(), + }; + assert!(err.contains(expected), "{err}"); + } + } } diff --git a/kernels-common/src/config/v6.rs b/kernels-common/src/config/v6.rs index 15535eae4..c4f621f41 100644 --- a/kernels-common/src/config/v6.rs +++ b/kernels-common/src/config/v6.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use monostate::MustBe; use serde::{Deserialize, Serialize}; -use super::{Dependency, GitUrl, KernelDependency, KernelName}; +use super::{ConfigError, Dependency, GitUrl, KernelDependency, KernelName}; use crate::version::Version; // `monostate` validates the edition on read but provides no `Serialize` impl for it. @@ -139,7 +139,11 @@ pub struct TorchNoarch { pub struct TvmFfi { pub include: Option>, pub pyext: Option>, + + // Rust-only kernels have no C++ binding code, so `src` may be omitted. + #[serde(default)] pub src: Vec, + pub cxx_flags: Option>, } @@ -156,6 +160,13 @@ pub enum Kernel { src: Vec, }, #[serde(rename_all = "kebab-case")] + RustCpu { + /// Path of the crate's `Cargo.toml`, relative to the kernel directory. + cargo_manifest: String, + depends: Vec, + src: Vec, + }, + #[serde(rename_all = "kebab-case")] CppCuda { cuda_capabilities: Option>, cuda_flags: Option>, @@ -204,19 +215,28 @@ pub enum Backend { Xpu, } -impl From for super::Build { - fn from(build: Build) -> Self { +impl TryFrom for super::Build { + type Error = ConfigError; + + fn try_from(build: Build) -> Result { + let tvm_ffi = matches!(build.framework, Framework::TvmFfi(_)); let kernels: HashMap = build .kernels .into_iter() - .map(|(k, v)| (k, v.into())) - .collect(); - - Self { + .map(|(name, kernel)| match kernel { + Kernel::RustCpu { .. } if !tvm_ffi => Err(ConfigError::InvalidKernel { + name, + reason: "Rust kernels require a `[tvm-ffi]` framework".into(), + }), + kernel => Ok((name, kernel.into())), + }) + .collect::>()?; + + Ok(Self { general: build.general.into(), framework: build.framework.into(), kernels, - } + }) } } @@ -362,6 +382,15 @@ impl From for super::Kernel { include, src, }, + Kernel::RustCpu { + cargo_manifest, + depends, + src, + } => super::Kernel::RustCpu { + cargo_manifest, + depends, + src, + }, Kernel::CppCuda { cuda_capabilities, cuda_flags, @@ -579,6 +608,15 @@ impl From for Kernel { include, src, }, + super::Kernel::RustCpu { + cargo_manifest, + depends, + src, + } => Kernel::RustCpu { + cargo_manifest, + depends, + src, + }, super::Kernel::CppCuda { cuda_capabilities, cuda_flags, diff --git a/nix-builder/lib/build.nix b/nix-builder/lib/build.nix index 0d6708a45..7bd5801d0 100644 --- a/nix-builder/lib/build.nix +++ b/nix-builder/lib/build.nix @@ -135,6 +135,9 @@ rec { kernelDeps = lib.unique (lib.flatten (lib.mapAttrsToList (_: kernel: kernel.depends) kernels)); in extension.resolveCppDeps kernelDeps; + hasRustKernels = lib.any (kernel: kernelConfig.kernelLanguage kernel == "rust") ( + lib.attrValues kernels + ); # Use the mkSourceSet function to get the source src = mkSourceSet path; @@ -185,6 +188,7 @@ rec { kernelProvenance ; + cargoLock = if hasRustKernels then src + "/Cargo.lock" else null; kernelName = kernelConfig.name; doAbiCheck = true; variant = variants.kernelVariant kernelConfig; diff --git a/nix-builder/lib/extension/tvm-ffi/arch.nix b/nix-builder/lib/extension/tvm-ffi/arch.nix index eab14d6f8..542582e97 100644 --- a/nix-builder/lib/extension/tvm-ffi/arch.nix +++ b/nix-builder/lib/extension/tvm-ffi/arch.nix @@ -10,6 +10,7 @@ # Native build inputs kernel-builder, + cargo, cmake, cmakeNvccThreadsHook, cuda_nvcc, @@ -20,6 +21,8 @@ python3, remove-bytecode-hook, rewrite-nix-paths-macho, + rustc, + rustPlatform, torch-ops-check, writeScriptBin, @@ -53,6 +56,10 @@ # Extra dependencies (such as CUTLASS). extraDeps ? [ ], + # Path to the `Cargo.lock` of the kernel's Rust crates, or `null` when + # the build has no Rust kernels. + cargoLock ? null, + nvccThreads, # Dependencies on other kernels. Path to a JSON file that maps @@ -122,6 +129,8 @@ let metalSupport = buildConfig.metal or false; + rustSupport = cargoLock != null; + provenanceFlags = import ../provenance-flags.nix { inherit lib kernelProvenance; }; in @@ -139,6 +148,11 @@ stdenv.mkDerivation (prevAttrs: { framework = "tvm-ffi"; + ${if rustSupport then "cargoDeps" else null} = rustPlatform.importCargoLock { + lockFile = cargoLock; + allowBuiltinFetchGit = true; + }; + # We run kernel-builder here rather than patchPhase or preConfigure, # so that external users of `src` get the source tree with the files # generated by kernel-builder. @@ -199,6 +213,11 @@ stdenv.mkDerivation (prevAttrs: { ]) ++ lib.optionals stdenv.hostPlatform.isDarwin [ rewrite-nix-paths-macho + ] + ++ lib.optionals rustSupport [ + rustPlatform.cargoSetupHook + cargo + rustc ]; buildInputs = [ diff --git a/nix-builder/lib/kernel-config.nix b/nix-builder/lib/kernel-config.nix index ddb841726..60c7ca58a 100644 --- a/nix-builder/lib/kernel-config.nix +++ b/nix-builder/lib/kernel-config.nix @@ -11,6 +11,7 @@ let buildToml; # Edition 6 tags kernels with `language = "-"`. + kernelLanguage = kernel: lib.head (lib.splitString "-" kernel.language); kernelBackend = kernel: lib.last (lib.splitString "-" kernel.language); toml = validate (readToml (path + "/build.toml")); @@ -26,7 +27,7 @@ let if builtins.isString stableAbi then stableAbi else stableAbi.${backend} or null; in { - inherit kernelBackend toml; + inherit kernelBackend kernelLanguage toml; # Is the kernel a Torch kernel. isTorch = toml ? torch; diff --git a/nix-builder/lib/source-set.nix b/nix-builder/lib/source-set.nix index f9683d307..9b76f443a 100644 --- a/nix-builder/lib/source-set.nix +++ b/nix-builder/lib/source-set.nix @@ -21,6 +21,7 @@ let torchExtPath = path + "/torch-ext"; tvmFfiExtPath = path + "/tvm-ffi-ext"; lockSet = fileset.maybeMissing (path + "/kernels.lock"); + cargoLockSet = fileset.maybeMissing (path + "/Cargo.lock"); pySrcSet = let path = @@ -40,8 +41,10 @@ let fileset.fileFilter pyFilter (path + "/tests") else fileset.empty; + # Rust kernels also need their Cargo manifest. + kernelSrc = kernel: kernel.src ++ lib.optional (kernel ? cargo-manifest) kernel.cargo-manifest; kernelsSrc = fileset.unions ( - lib.flatten (lib.mapAttrsToList (name: buildConfig: map (nameToPath path) buildConfig.src) kernels) + lib.flatten (lib.mapAttrsToList (name: kernel: map (nameToPath path) (kernelSrc kernel)) kernels) ); srcSet = fileset.unions (map (nameToPath path) extSrc); in @@ -50,6 +53,7 @@ fileset.toSource { fileset = fileset.unions [ kernelsSrc lockSet + cargoLockSet srcSet pySrcSet pyTestsSet From 3ab4c84f20a11a773057c0ac90f2602054246ca1 Mon Sep 17 00:00:00 2001 From: David Holtz Date: Mon, 5 Oct 2026 16:52:48 +0000 Subject: [PATCH 2/4] fix: pin cargo hashes --- docs/source/builder/writing-kernels.md | 13 ++ examples/kernels/relu-rust/Cargo.lock | 6 +- examples/kernels/relu-rust/relu-rs/Cargo.toml | 3 +- .../kernels/relu-rust/rust-git-hashes.json | 5 + nix-builder/lib/build.nix | 5 + nix-builder/lib/extension/tvm-ffi/arch.nix | 13 +- nix-builder/lib/gen-flake-outputs.nix | 7 + nix-builder/lib/source-set.nix | 2 + nix-builder/overlay.nix | 2 + nix-builder/pkgs/hash-rust/default.nix | 16 +++ nix-builder/pkgs/hash-rust/hash_rust.py | 66 +++++++++ nix-builder/pkgs/hash-rust/test_hash_rust.py | 132 ++++++++++++++++++ 12 files changed, 261 insertions(+), 9 deletions(-) create mode 100644 examples/kernels/relu-rust/rust-git-hashes.json create mode 100644 nix-builder/pkgs/hash-rust/default.nix create mode 100644 nix-builder/pkgs/hash-rust/hash_rust.py create mode 100644 nix-builder/pkgs/hash-rust/test_hash_rust.py diff --git a/docs/source/builder/writing-kernels.md b/docs/source/builder/writing-kernels.md index 1f782bf69..48c9bddfe 100644 --- a/docs/source/builder/writing-kernels.md +++ b/docs/source/builder/writing-kernels.md @@ -364,6 +364,19 @@ Rust kernels are built with Cargo and require the `tvm-ffi` framework. - `cargo-manifest` (required): path of the crate's `Cargo.toml`. +Place `Cargo.lock` at the kernel project root. If it contains Git dependencies, +generate their fixed-output Nix hashes from that directory: + +```bash +nix run .#hash-rust +``` + +This command is available for Rust kernels, reads `Cargo.lock` from the current +directory, and writes `rust-git-hashes.json` there. Track both files +in Git so Nix can include them in the build. Rerun the command whenever Git +dependencies in `Cargo.lock` change. Registry dependencies use the checksums in +`Cargo.lock` and do not require this file. + ## Torch bindings ### Defining bindings diff --git a/examples/kernels/relu-rust/Cargo.lock b/examples/kernels/relu-rust/Cargo.lock index af709f9b9..3b8fb0a38 100644 --- a/examples/kernels/relu-rust/Cargo.lock +++ b/examples/kernels/relu-rust/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "tvm-ffi" version = "0.1.0-alpha.0" -source = "git+https://github.com/apache/tvm-ffi.git?rev=2af558e255ff2f398095835ae18e6457635b0262#2af558e255ff2f398095835ae18e6457635b0262" +source = "git+https://github.com/apache/tvm-ffi.git?tag=v0.1.14-post1#e251c6abee7a111c6467f84d854be512e33e2db0" dependencies = [ "paste", "tvm-ffi-macros", @@ -81,7 +81,7 @@ dependencies = [ [[package]] name = "tvm-ffi-macros" version = "0.1.0-alpha.0" -source = "git+https://github.com/apache/tvm-ffi.git?rev=2af558e255ff2f398095835ae18e6457635b0262#2af558e255ff2f398095835ae18e6457635b0262" +source = "git+https://github.com/apache/tvm-ffi.git?tag=v0.1.14-post1#e251c6abee7a111c6467f84d854be512e33e2db0" dependencies = [ "proc-macro-error", "proc-macro2", @@ -92,7 +92,7 @@ dependencies = [ [[package]] name = "tvm-ffi-sys" version = "0.1.0-alpha.0" -source = "git+https://github.com/apache/tvm-ffi.git?rev=2af558e255ff2f398095835ae18e6457635b0262#2af558e255ff2f398095835ae18e6457635b0262" +source = "git+https://github.com/apache/tvm-ffi.git?tag=v0.1.14-post1#e251c6abee7a111c6467f84d854be512e33e2db0" [[package]] name = "unicode-ident" diff --git a/examples/kernels/relu-rust/relu-rs/Cargo.toml b/examples/kernels/relu-rust/relu-rs/Cargo.toml index 2717eac90..8a77e2214 100644 --- a/examples/kernels/relu-rust/relu-rs/Cargo.toml +++ b/examples/kernels/relu-rust/relu-rs/Cargo.toml @@ -9,4 +9,5 @@ publish = false name = "relu_rust" [dependencies] -tvm-ffi = { git = "https://github.com/apache/tvm-ffi.git", rev = "2af558e255ff2f398095835ae18e6457635b0262" } +# TODO: prefer using crate when published - pinning the release so moving to the crate should be frictionless. +tvm-ffi = { git = "https://github.com/apache/tvm-ffi.git", tag = "v0.1.14-post1" } diff --git a/examples/kernels/relu-rust/rust-git-hashes.json b/examples/kernels/relu-rust/rust-git-hashes.json new file mode 100644 index 000000000..ae8fed57b --- /dev/null +++ b/examples/kernels/relu-rust/rust-git-hashes.json @@ -0,0 +1,5 @@ +{ + "tvm-ffi-0.1.0-alpha.0": "sha256-HoyzO0koOBH7ZVb29Mhxjx8NPHxRfOHccvjNR6AHmSg=", + "tvm-ffi-macros-0.1.0-alpha.0": "sha256-HoyzO0koOBH7ZVb29Mhxjx8NPHxRfOHccvjNR6AHmSg=", + "tvm-ffi-sys-0.1.0-alpha.0": "sha256-HoyzO0koOBH7ZVb29Mhxjx8NPHxRfOHccvjNR6AHmSg=" +} diff --git a/nix-builder/lib/build.nix b/nix-builder/lib/build.nix index 7bd5801d0..a321597e7 100644 --- a/nix-builder/lib/build.nix +++ b/nix-builder/lib/build.nix @@ -189,6 +189,11 @@ rec { ; cargoLock = if hasRustKernels then src + "/Cargo.lock" else null; + rustGitHashes = + if hasRustKernels && builtins.pathExists (src + "/rust-git-hashes.json") then + builtins.fromJSON (builtins.readFile (src + "/rust-git-hashes.json")) + else + { }; kernelName = kernelConfig.name; doAbiCheck = true; variant = variants.kernelVariant kernelConfig; diff --git a/nix-builder/lib/extension/tvm-ffi/arch.nix b/nix-builder/lib/extension/tvm-ffi/arch.nix index 542582e97..c40cb3f20 100644 --- a/nix-builder/lib/extension/tvm-ffi/arch.nix +++ b/nix-builder/lib/extension/tvm-ffi/arch.nix @@ -60,6 +60,9 @@ # the build has no Rust kernels. cargoLock ? null, + # Fixed-output hashes for Git dependencies in Cargo.lock. + rustGitHashes ? { }, + nvccThreads, # Dependencies on other kernels. Path to a JSON file that maps @@ -148,11 +151,6 @@ stdenv.mkDerivation (prevAttrs: { framework = "tvm-ffi"; - ${if rustSupport then "cargoDeps" else null} = rustPlatform.importCargoLock { - lockFile = cargoLock; - allowBuiltinFetchGit = true; - }; - # We run kernel-builder here rather than patchPhase or preConfigure, # so that external users of `src` get the source tree with the files # generated by kernel-builder. @@ -340,4 +338,9 @@ stdenv.mkDerivation (prevAttrs: { inherit (python3.pkgs.tvm-ffi) variant; archVariant = python3.pkgs.tvm-ffi.variant; }; +} // lib.optionalAttrs rustSupport { + cargoDeps = rustPlatform.importCargoLock { + lockFile = cargoLock; + outputHashes = rustGitHashes; + }; }) diff --git a/nix-builder/lib/gen-flake-outputs.nix b/nix-builder/lib/gen-flake-outputs.nix index 53b05f7f9..4cb604d13 100644 --- a/nix-builder/lib/gen-flake-outputs.nix +++ b/nix-builder/lib/gen-flake-outputs.nix @@ -46,6 +46,10 @@ let kernelConfig = (import ./kernel-config.nix { inherit lib; }) path; + hasRustKernels = lib.any (kernel: kernelConfig.kernelLanguage kernel == "rust") ( + lib.attrValues (kernelConfig.toml.kernel or { }) + ); + buildConfigBackend = buildConfig: if buildConfig.cpu or false then @@ -325,5 +329,8 @@ in archVariants = lib.unique ( map (buildSet: buildSet.variants.kernelArchVariant kernelConfig) applicableBuildSets ); + } + // lib.optionalAttrs hasRustKernels { + inherit (pkgs) hash-rust; }; } diff --git a/nix-builder/lib/source-set.nix b/nix-builder/lib/source-set.nix index 9b76f443a..a1d759092 100644 --- a/nix-builder/lib/source-set.nix +++ b/nix-builder/lib/source-set.nix @@ -22,6 +22,7 @@ let tvmFfiExtPath = path + "/tvm-ffi-ext"; lockSet = fileset.maybeMissing (path + "/kernels.lock"); cargoLockSet = fileset.maybeMissing (path + "/Cargo.lock"); + rustGitHashesSet = fileset.maybeMissing (path + "/rust-git-hashes.json"); pySrcSet = let path = @@ -54,6 +55,7 @@ fileset.toSource { kernelsSrc lockSet cargoLockSet + rustGitHashesSet srcSet pySrcSet pyTestsSet diff --git a/nix-builder/overlay.nix b/nix-builder/overlay.nix index 5fd4126c4..b67eea9bd 100644 --- a/nix-builder/overlay.nix +++ b/nix-builder/overlay.nix @@ -22,6 +22,8 @@ final: prev: hash-kernel-hook = final.callPackage ./pkgs/hash-kernel-hook { }; + hash-rust = final.callPackage ./pkgs/hash-rust { }; + kernel-layout-check = final.callPackage ./pkgs/kernel-layout-check { }; # The same Cargo.lock is used by kernel-builder and the kernels Python diff --git a/nix-builder/pkgs/hash-rust/default.nix b/nix-builder/pkgs/hash-rust/default.nix new file mode 100644 index 000000000..5f1799c4a --- /dev/null +++ b/nix-builder/pkgs/hash-rust/default.nix @@ -0,0 +1,16 @@ +{ + writeShellApplication, + python3, + nix-prefetch-git, +}: + +writeShellApplication { + name = "hash-rust"; + runtimeInputs = [ + python3 + nix-prefetch-git + ]; + text = '' + exec python3 ${./hash_rust.py} "$@" + ''; +} diff --git a/nix-builder/pkgs/hash-rust/hash_rust.py b/nix-builder/pkgs/hash-rust/hash_rust.py new file mode 100644 index 000000000..ad83025ec --- /dev/null +++ b/nix-builder/pkgs/hash-rust/hash_rust.py @@ -0,0 +1,66 @@ +import argparse +import json +import subprocess +import tempfile +import tomllib +from pathlib import Path +from string import hexdigits +from urllib.parse import urlsplit + + +def git_hashes(lock_file: Path) -> dict[str, str]: + with lock_file.open("rb") as file: + lock = tomllib.load(file) + hashes = {} + fetched = {} + for package in lock.get("package", []): + source = package.get("source", "") + if not source.startswith("git+"): + continue + parts = urlsplit(source.removeprefix("git+")) + revision = parts.fragment + if len(revision) != 40 or any(char not in hexdigits for char in revision): + raise ValueError(f"Unsupported Cargo Git source: {source}") + url = parts._replace(query="", fragment="").geturl() + key = (url, revision) + if key not in fetched: + result = subprocess.run( + [ + "nix-prefetch-git", + "--url", + url, + "--rev", + revision, + "--fetch-submodules", + ], + check=True, + stdout=subprocess.PIPE, + text=True, + ) + fetched[key] = json.loads(result.stdout)["hash"] + name = f"{package['name']}-{package['version']}" + if name in hashes and hashes[name] != fetched[key]: + raise ValueError(f"Conflicting Git hashes for {name}") + hashes[name] = fetched[key] + return hashes + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Generate rust-git-hashes.json from ./Cargo.lock." + ) + parser.parse_args() + try: + hashes = git_hashes(Path("Cargo.lock")) + # Keep the temporary file on the same filesystem for atomic replacement. + with tempfile.TemporaryDirectory(prefix=".rust-git-hashes-", dir=".") as tmp: + output = Path(tmp) / "hashes.json" + output.write_text(json.dumps(hashes, indent=2, sort_keys=True) + "\n") + output.replace("rust-git-hashes.json") + except (OSError, ValueError, subprocess.CalledProcessError) as error: + parser.exit(1, f"hash-rust: {error}\n") + print("Wrote rust-git-hashes.json") + + +if __name__ == "__main__": + main() diff --git a/nix-builder/pkgs/hash-rust/test_hash_rust.py b/nix-builder/pkgs/hash-rust/test_hash_rust.py new file mode 100644 index 000000000..ff57478aa --- /dev/null +++ b/nix-builder/pkgs/hash-rust/test_hash_rust.py @@ -0,0 +1,132 @@ +import contextlib +import io +import json +import subprocess +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from hash_rust import git_hashes, main + + +class HashRustTests(unittest.TestCase): + def setUp(self): + stack = contextlib.ExitStack() + self.addCleanup(stack.close) + directory = stack.enter_context(tempfile.TemporaryDirectory()) + stack.enter_context(contextlib.chdir(directory)) + self.lock = Path("Cargo.lock") + self.lock.write_text( + 'version = 4\n[[package]]\nname = "local"\nversion = "1"\n' + ) + + def test_local_and_registry_dependencies_need_no_fetch(self): + self.lock.write_text( + self.lock.read_text() + + '[[package]]\nname = "registry"\nversion = "1"\n' + + 'source = "registry+https://example.org"\n' + ) + with patch("hash_rust.subprocess.run") as fetch: + self.assertEqual(git_hashes(self.lock), {}) + fetch.assert_not_called() + + def test_workspace_dependencies_share_fetch_and_use_resolved_commit(self): + revision = "a" * 40 + self.lock.write_text( + "\n".join( + f'[[package]]\nname = "{name}"\nversion = "1"\n' + f'source = "git+https://example.org/repo{query}#{revision}"' + for name, query in [ + ("one", "?tag=v1"), + ("two", "?branch=main"), + ("three", ""), + ] + ) + ) + sri = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + with patch("hash_rust.subprocess.run") as fetch: + fetch.return_value.stdout = json.dumps({"hash": sri}) + self.assertEqual( + git_hashes(self.lock), + {f"{name}-1": sri for name in ["one", "two", "three"]}, + ) + fetch.assert_called_once_with( + [ + "nix-prefetch-git", + "--url", + "https://example.org/repo", + "--rev", + revision, + "--fetch-submodules", + ], + check=True, + stdout=subprocess.PIPE, + text=True, + ) + + def test_unpinned_source_is_rejected(self): + for revision in ["", "main", "a" * 39, "g" * 40]: + with self.subTest(revision=revision): + self.lock.write_text( + '[[package]]\nname = "crate"\nversion = "1"\n' + f'source = "git+https://example.org/repo?branch=main#{revision}"\n' + ) + with patch("hash_rust.subprocess.run") as fetch: + with self.assertRaises(ValueError): + git_hashes(self.lock) + fetch.assert_not_called() + + def test_url_parsing_preserves_repository_and_ignores_selectors(self): + revision = "a" * 40 + for url in [ + "https://example.org/repo.git", + "ssh://git@example.org:2222/repo.git", + "file:///tmp/repo", + ]: + with self.subTest(url=url): + self.lock.write_text( + '[[package]]\nname = "crate"\nversion = "1"\n' + f'source = "git+{url}?branch=feature%2Ffix%23issue#{revision}"\n' + ) + with patch("hash_rust.subprocess.run") as fetch: + fetch.return_value.stdout = '{"hash": "sha256-test"}' + self.assertEqual(git_hashes(self.lock), {"crate-1": "sha256-test"}) + self.assertEqual( + fetch.call_args.args[0], + [ + "nix-prefetch-git", + "--url", + url, + "--rev", + revision, + "--fetch-submodules", + ], + ) + + def test_failure_preserves_output_and_success_removes_stale_hashes(self): + output = Path("rust-git-hashes.json") + original = '{"stale-1": "old-hash"}\n' + output.write_text(original) + with patch("sys.argv", ["hash-rust"]): + with patch( + "hash_rust.git_hashes", + side_effect=subprocess.CalledProcessError(1, "fetch"), + ): + with ( + contextlib.redirect_stderr(io.StringIO()), + self.assertRaises(SystemExit) as error, + ): + main() + self.assertEqual(error.exception.code, 1) + self.assertEqual(output.read_text(), original) + main() + self.assertEqual(json.loads(output.read_text()), {}) + self.assertEqual( + sorted(p.name for p in Path(".").iterdir()), + ["Cargo.lock", "rust-git-hashes.json"], + ) + + +if __name__ == "__main__": + unittest.main() From ed61ae539e3fc2b62c69d7ec510700c15f1247ea Mon Sep 17 00:00:00 2001 From: David Holtz Date: Mon, 5 Oct 2026 17:05:51 +0000 Subject: [PATCH 3/4] fix: rerun nixfmt --- nix-builder/lib/extension/tvm-ffi/arch.nix | 398 +++++++++++---------- 1 file changed, 201 insertions(+), 197 deletions(-) diff --git a/nix-builder/lib/extension/tvm-ffi/arch.nix b/nix-builder/lib/extension/tvm-ffi/arch.nix index c40cb3f20..d1cdc3c4a 100644 --- a/nix-builder/lib/extension/tvm-ffi/arch.nix +++ b/nix-builder/lib/extension/tvm-ffi/arch.nix @@ -138,209 +138,213 @@ let in -stdenv.mkDerivation (prevAttrs: { - name = "${kernelName}-tvm-ffi-ext"; - - inherit - doAbiCheck - doKernelBuildCheck - kernelDeps - moduleName - nvccThreads - ; - - framework = "tvm-ffi"; - - # We run kernel-builder here rather than patchPhase or preConfigure, - # so that external users of `src` get the source tree with the files - # generated by kernel-builder. - src = pkgs.runCommand "source" { } '' - mkdir -p $out - cp -r --no-preserve=mode ${src}/* $out/ - ${pkgs.kernel-builder}/bin/kernel-builder create-pyproject \ - --unique-id ${rev} ${provenanceFlags} $out - ''; +stdenv.mkDerivation ( + prevAttrs: + { + name = "${kernelName}-tvm-ffi-ext"; + + inherit + doAbiCheck + doKernelBuildCheck + kernelDeps + moduleName + nvccThreads + ; + + framework = "tvm-ffi"; + + # We run kernel-builder here rather than patchPhase or preConfigure, + # so that external users of `src` get the source tree with the files + # generated by kernel-builder. + src = pkgs.runCommand "source" { } '' + mkdir -p $out + cp -r --no-preserve=mode ${src}/* $out/ + ${pkgs.kernel-builder}/bin/kernel-builder create-pyproject \ + --unique-id ${rev} ${provenanceFlags} $out + ''; - preConfigure = - # This is a workaround for https://openradar.appspot.com/FB20389216 - even - # if the user downloaded the Metal toolchain, the mapping is not set up - # for the Nix build users. To make things worse, we cannot set up a mapping - # because the Nix build users do not have a writable home directory and - # showComponent/downloadComponent do not respect the HOME variable. So - # instead, we'll use showComponent (which will emit a lot of warnings due - # to the above) to grab the path of the Metal toolchain. - lib.optionalString metalSupport '' - METAL_PATH=$(${xcrunHost}/bin/xcrunHost xcodebuild -showComponent MetalToolchain 2> /dev/null | sed -rn "s/Toolchain Search Path: (.*)/\1/p") - if [ ! -d "$METAL_PATH" ]; then - >&2 echo "Cannot find Metal toolchain, use: xcodebuild -downloadComponent MetalToolchain" - exit 1 - fi - - cmakeFlagsArray+=("-DMETAL_TOOLCHAIN=$METAL_PATH/Metal.xctoolchain") + preConfigure = + # This is a workaround for https://openradar.appspot.com/FB20389216 - even + # if the user downloaded the Metal toolchain, the mapping is not set up + # for the Nix build users. To make things worse, we cannot set up a mapping + # because the Nix build users do not have a writable home directory and + # showComponent/downloadComponent do not respect the HOME variable. So + # instead, we'll use showComponent (which will emit a lot of warnings due + # to the above) to grab the path of the Metal toolchain. + lib.optionalString metalSupport '' + METAL_PATH=$(${xcrunHost}/bin/xcrunHost xcodebuild -showComponent MetalToolchain 2> /dev/null | sed -rn "s/Toolchain Search Path: (.*)/\1/p") + if [ ! -d "$METAL_PATH" ]; then + >&2 echo "Cannot find Metal toolchain, use: xcodebuild -downloadComponent MetalToolchain" + exit 1 + fi + + cmakeFlagsArray+=("-DMETAL_TOOLCHAIN=$METAL_PATH/Metal.xctoolchain") + ''; + + # hipify copies files, but its target is run in the CMake build and install + # phases. Since some of the files come from the Nix store, this fails the + # second time around. + preInstall = '' + chmod -R u+w . ''; - # hipify copies files, but its target is run in the CMake build and install - # phases. Since some of the files come from the Nix store, this fails the - # second time around. - preInstall = '' - chmod -R u+w . - ''; + nativeBuildInputs = [ + cmake + hash-kernel-hook + ninja + kernel-builder + kernel-layout-check + remove-bytecode-hook + torch-ops-check + ] + ++ lib.optionals doGetKernelCheck [ + (get-kernel-check.override { python3 = python3.withPackages (ps: dependencies); }) + ] + ++ lib.optionals cudaSupport [ + cmakeNvccThreadsHook + cuda_nvcc + ] + ++ lib.optionals rocmSupport [ + clr + ] + ++ lib.optionals xpuSupport ([ + xpuPackages.ocloc + oneapi-torch-dev + ]) + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + rewrite-nix-paths-macho + ] + ++ lib.optionals rustSupport [ + rustPlatform.cargoSetupHook + cargo + rustc + ]; - nativeBuildInputs = [ - cmake - hash-kernel-hook - ninja - kernel-builder - kernel-layout-check - remove-bytecode-hook - torch-ops-check - ] - ++ lib.optionals doGetKernelCheck [ - (get-kernel-check.override { python3 = python3.withPackages (ps: dependencies); }) - ] - ++ lib.optionals cudaSupport [ - cmakeNvccThreadsHook - cuda_nvcc - ] - ++ lib.optionals rocmSupport [ - clr - ] - ++ lib.optionals xpuSupport ([ - xpuPackages.ocloc - oneapi-torch-dev - ]) - ++ lib.optionals stdenv.hostPlatform.isDarwin [ - rewrite-nix-paths-macho - ] - ++ lib.optionals rustSupport [ - rustPlatform.cargoSetupHook - cargo - rustc - ]; - - buildInputs = [ - python3.pkgs.tvm-ffi - ] - ++ lib.optionals cudaSupport ( - with cudaPackages; - [ - cuda_cudart - - # Make dependent on build configuration dependencies once - # the Torch dependency is gone. - cccl - libcublas - libcusolver - libcusparse + buildInputs = [ + python3.pkgs.tvm-ffi ] - ) - ++ lib.optionals rocmSupport ( - with rocmPackages; - if theRock then - [ - amdrocm-ccl-devel - amdrocm-hipblas-common-devel - ] - else + ++ lib.optionals cudaSupport ( + with cudaPackages; [ - hipcub-devel - hipsparselt - rocprim-devel - rocthrust-devel - rocshmem-devel - rocwmma-devel + cuda_cudart + + # Make dependent on build configuration dependencies once + # the Torch dependency is gone. + cccl + libcublas + libcusolver + libcusparse ] - ) - ++ lib.optionals xpuSupport ([ - oneapi-torch-dev - onednn-xpu - ]) - ++ lib.optionals stdenv.hostPlatform.isDarwin [ - apple-sdk_26 - ] - ++ extraDeps; - - env = { - inherit variant; - } - // lib.optionalAttrs cudaSupport { - CUDAToolkit_ROOT = "${lib.getDev cudaPackages.cuda_nvcc}"; - } - // lib.optionalAttrs xpuSupport { - MKLROOT = oneapi-torch-dev; - SYCL_ROOT = oneapi-torch-dev; - } - // lib.optionalAttrs stdenv.hostPlatform.isDarwin { - # Without LANG set, libintl queries CoreFoundation for the preferred - # languages, which intermittendly segfaults the build sandbox. - LANG = "C"; - }; - - # If we use the default setup, CMAKE_CUDA_HOST_COMPILER gets set to nixpkgs g++. - dontSetupCUDAToolkitCompilers = true; - - cmakeFlags = [ - (lib.cmakeBool "BUILD_ALL_SUPPORTED_ARCHS" true) - (lib.cmakeFeature "Python_EXECUTABLE" "${ - python3.withPackages ( - ps: with ps; [ - tvm-ffi - typing-extensions + ) + ++ lib.optionals rocmSupport ( + with rocmPackages; + if theRock then + [ + amdrocm-ccl-devel + amdrocm-hipblas-common-devel ] - ) - }/bin/python") - # Fix: file RPATH_CHANGE could not write new RPATH, we are rewriting - # rpaths anyway. - (lib.cmakeBool "CMAKE_SKIP_RPATH" true) - ] - ++ lib.optionals cudaSupport [ - (lib.cmakeFeature "CMAKE_CUDA_HOST_COMPILER" "${stdenv.cc}/bin/g++") - (lib.cmakeFeature "GPU_LANG" "CUDA") - ] - ++ lib.optionals rocmSupport [ - # Ensure sure that we use HIP from our CLR override and not HIP from - # the symlink-joined ROCm toolkit. - (lib.cmakeFeature "CMAKE_HIP_COMPILER_ROCM_ROOT" "${clr}") - (lib.cmakeFeature "HIP_ROOT_DIR" "${clr}") - ] - ++ lib.optionals metalSupport [ - # Use host compiler for Metal. Not included in the redistributable SDK. - # Re-enable when the issue mentioned in preConfigure is solved. - #(lib.cmakeFeature "METAL_COMPILER" "${xcrunHost}/bin/xcrunHost") - ] - ++ lib.optionals xpuSupport [ - (lib.cmakeFeature "GPU_LANG" "SYCL") - ]; - - postInstall = '' - rm -rf $out/_${moduleName}_*_${rev} - '' - + (lib.optionalString (stripRPath && stdenv.hostPlatform.isLinux)) '' - find $out/ -name '*.so' \ - -exec patchelf --set-rpath "" {} \; - '' - + (lib.optionalString (stripRPath && stdenv.hostPlatform.isDarwin)) '' - find $out/ -name '*.so' \ - -exec rewrite-nix-paths-macho {} \; - - # Stub some rpath. - find $out/ -name '*.so' \ - -exec install_name_tool -add_rpath "@loader_path/lib" {} \; - ''; + else + [ + hipcub-devel + hipsparselt + rocprim-devel + rocthrust-devel + rocshmem-devel + rocwmma-devel + ] + ) + ++ lib.optionals xpuSupport ([ + oneapi-torch-dev + onednn-xpu + ]) + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + apple-sdk_26 + ] + ++ extraDeps; + + env = { + inherit variant; + } + // lib.optionalAttrs cudaSupport { + CUDAToolkit_ROOT = "${lib.getDev cudaPackages.cuda_nvcc}"; + } + // lib.optionalAttrs xpuSupport { + MKLROOT = oneapi-torch-dev; + SYCL_ROOT = oneapi-torch-dev; + } + // lib.optionalAttrs stdenv.hostPlatform.isDarwin { + # Without LANG set, libintl queries CoreFoundation for the preferred + # languages, which intermittendly segfaults the build sandbox. + LANG = "C"; + }; + + # If we use the default setup, CMAKE_CUDA_HOST_COMPILER gets set to nixpkgs g++. + dontSetupCUDAToolkitCompilers = true; + + cmakeFlags = [ + (lib.cmakeBool "BUILD_ALL_SUPPORTED_ARCHS" true) + (lib.cmakeFeature "Python_EXECUTABLE" "${ + python3.withPackages ( + ps: with ps; [ + tvm-ffi + typing-extensions + ] + ) + }/bin/python") + # Fix: file RPATH_CHANGE could not write new RPATH, we are rewriting + # rpaths anyway. + (lib.cmakeBool "CMAKE_SKIP_RPATH" true) + ] + ++ lib.optionals cudaSupport [ + (lib.cmakeFeature "CMAKE_CUDA_HOST_COMPILER" "${stdenv.cc}/bin/g++") + (lib.cmakeFeature "GPU_LANG" "CUDA") + ] + ++ lib.optionals rocmSupport [ + # Ensure sure that we use HIP from our CLR override and not HIP from + # the symlink-joined ROCm toolkit. + (lib.cmakeFeature "CMAKE_HIP_COMPILER_ROCM_ROOT" "${clr}") + (lib.cmakeFeature "HIP_ROOT_DIR" "${clr}") + ] + ++ lib.optionals metalSupport [ + # Use host compiler for Metal. Not included in the redistributable SDK. + # Re-enable when the issue mentioned in preConfigure is solved. + #(lib.cmakeFeature "METAL_COMPILER" "${xcrunHost}/bin/xcrunHost") + ] + ++ lib.optionals xpuSupport [ + (lib.cmakeFeature "GPU_LANG" "SYCL") + ]; + + postInstall = '' + rm -rf $out/_${moduleName}_*_${rev} + '' + + (lib.optionalString (stripRPath && stdenv.hostPlatform.isLinux)) '' + find $out/ -name '*.so' \ + -exec patchelf --set-rpath "" {} \; + '' + + (lib.optionalString (stripRPath && stdenv.hostPlatform.isDarwin)) '' + find $out/ -name '*.so' \ + -exec rewrite-nix-paths-macho {} \; + + # Stub some rpath. + find $out/ -name '*.so' \ + -exec install_name_tool -add_rpath "@loader_path/lib" {} \; + ''; - doInstallCheck = true; - - # We need access to the host system on Darwin for the Metal compiler. - __noChroot = metalSupport; - - passthru = { - inherit dependencies; - inherit (python3.pkgs.tvm-ffi) variant; - archVariant = python3.pkgs.tvm-ffi.variant; - }; -} // lib.optionalAttrs rustSupport { - cargoDeps = rustPlatform.importCargoLock { - lockFile = cargoLock; - outputHashes = rustGitHashes; - }; -}) + doInstallCheck = true; + + # We need access to the host system on Darwin for the Metal compiler. + __noChroot = metalSupport; + + passthru = { + inherit dependencies; + inherit (python3.pkgs.tvm-ffi) variant; + archVariant = python3.pkgs.tvm-ffi.variant; + }; + } + // lib.optionalAttrs rustSupport { + cargoDeps = rustPlatform.importCargoLock { + lockFile = cargoLock; + outputHashes = rustGitHashes; + }; + } +) From 7627ca3bf67197595b54ff5310e12f4acc9203ed Mon Sep 17 00:00:00 2001 From: David Holtz Date: Tue, 6 Oct 2026 14:35:14 +0000 Subject: [PATCH 4/4] fix: prefer crate over git dep --- examples/kernels/relu-rust/Cargo.lock | 15 +++++++++------ examples/kernels/relu-rust/relu-rs/Cargo.toml | 3 +-- examples/kernels/relu-rust/rust-git-hashes.json | 5 ----- 3 files changed, 10 insertions(+), 13 deletions(-) delete mode 100644 examples/kernels/relu-rust/rust-git-hashes.json diff --git a/examples/kernels/relu-rust/Cargo.lock b/examples/kernels/relu-rust/Cargo.lock index 3b8fb0a38..eb6a1fd69 100644 --- a/examples/kernels/relu-rust/Cargo.lock +++ b/examples/kernels/relu-rust/Cargo.lock @@ -70,8 +70,9 @@ dependencies = [ [[package]] name = "tvm-ffi" -version = "0.1.0-alpha.0" -source = "git+https://github.com/apache/tvm-ffi.git?tag=v0.1.14-post1#e251c6abee7a111c6467f84d854be512e33e2db0" +version = "0.1.14-post1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2573e03b4bc46bfb606b2801c7a94d332b7640acbe5936a0c64935d664ff9875" dependencies = [ "paste", "tvm-ffi-macros", @@ -80,8 +81,9 @@ dependencies = [ [[package]] name = "tvm-ffi-macros" -version = "0.1.0-alpha.0" -source = "git+https://github.com/apache/tvm-ffi.git?tag=v0.1.14-post1#e251c6abee7a111c6467f84d854be512e33e2db0" +version = "0.1.14-post1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c0f660be122ead0def43f986c9ec4aa635bb9e285820adbc449b8230426933f" dependencies = [ "proc-macro-error", "proc-macro2", @@ -91,8 +93,9 @@ dependencies = [ [[package]] name = "tvm-ffi-sys" -version = "0.1.0-alpha.0" -source = "git+https://github.com/apache/tvm-ffi.git?tag=v0.1.14-post1#e251c6abee7a111c6467f84d854be512e33e2db0" +version = "0.1.14-post1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b29bc4a713c7e86bcb98e1655832058c272eb336f8d62cece8344cb2666ee2" [[package]] name = "unicode-ident" diff --git a/examples/kernels/relu-rust/relu-rs/Cargo.toml b/examples/kernels/relu-rust/relu-rs/Cargo.toml index 8a77e2214..bd37710e7 100644 --- a/examples/kernels/relu-rust/relu-rs/Cargo.toml +++ b/examples/kernels/relu-rust/relu-rs/Cargo.toml @@ -9,5 +9,4 @@ publish = false name = "relu_rust" [dependencies] -# TODO: prefer using crate when published - pinning the release so moving to the crate should be frictionless. -tvm-ffi = { git = "https://github.com/apache/tvm-ffi.git", tag = "v0.1.14-post1" } +tvm-ffi = "=0.1.14-post1" diff --git a/examples/kernels/relu-rust/rust-git-hashes.json b/examples/kernels/relu-rust/rust-git-hashes.json deleted file mode 100644 index ae8fed57b..000000000 --- a/examples/kernels/relu-rust/rust-git-hashes.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tvm-ffi-0.1.0-alpha.0": "sha256-HoyzO0koOBH7ZVb29Mhxjx8NPHxRfOHccvjNR6AHmSg=", - "tvm-ffi-macros-0.1.0-alpha.0": "sha256-HoyzO0koOBH7ZVb29Mhxjx8NPHxRfOHccvjNR6AHmSg=", - "tvm-ffi-sys-0.1.0-alpha.0": "sha256-HoyzO0koOBH7ZVb29Mhxjx8NPHxRfOHccvjNR6AHmSg=" -}