From fbf7a7ae35672c70952e6bdeeee197e77a40270a Mon Sep 17 00:00:00 2001 From: yagna-1 Date: Tue, 26 May 2026 11:10:28 +0530 Subject: [PATCH 1/2] cuda-bindings: handle CUDA header discovery errors Report missing or unreadable CUDA headers directly from the build script and discover CUDA headers and libraries in both the standard toolkit layout and target-specific layouts such as targets/x86_64-linux and targets/sbsa-linux. Signed-off-by: yagna-1 --- crates/cuda-bindings/build.rs | 60 ++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/crates/cuda-bindings/build.rs b/crates/cuda-bindings/build.rs index fa7f587d..300eca62 100644 --- a/crates/cuda-bindings/build.rs +++ b/crates/cuda-bindings/build.rs @@ -32,22 +32,28 @@ fn run() -> Result<(), Box> { println!("cargo::rustc-check-cfg=cfg(cuda_has_cuEventElapsedTime_v2)"); let toolkit = cuda_toolkit_dir(); - let cuda_h = Path::new(&toolkit).join("include/cuda.h"); + let include_paths = collect_include_paths(&toolkit); + let cuda_h = include_paths + .iter() + .map(|include| include.join("cuda.h")) + .find(|path| path.is_file()) + .ok_or_else(|| { + format!( + "cuda-bindings: could not find cuda.h under {}. Set CUDA_TOOLKIT_PATH or CUDA_HOME to a CUDA Toolkit install that contains include/cuda.h or targets/x86_64-linux/include/cuda.h.", + toolkit + ) + })?; println!("cargo:rerun-if-changed={}", cuda_h.display()); - match std::fs::read_to_string(&cuda_h) { - Ok(contents) => { - if contents.contains("cuEventElapsedTime_v2") { - println!("cargo:rustc-cfg=cuda_has_cuEventElapsedTime_v2"); - } - } - Err(err) => { - println!( - "cargo:warning=cuda-bindings: Could not read cuda.h at {}: {}", - cuda_h.display(), - err - ); - } + let contents = std::fs::read_to_string(&cuda_h).map_err(|err| { + format!( + "cuda-bindings: could not read cuda.h at {}: {}", + cuda_h.display(), + err + ) + })?; + if contents.contains("cuEventElapsedTime_v2") { + println!("cargo:rustc-cfg=cuda_has_cuEventElapsedTime_v2"); } for path in collect_lib_paths(&toolkit) { @@ -55,9 +61,8 @@ fn run() -> Result<(), Box> { } println!("cargo:rustc-link-lib=dylib=cuda"); - bindgen::builder() + let mut builder = bindgen::builder() .header("wrapper.h") - .clang_arg(format!("-I{}/include", toolkit)) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) // CUDA 13.2+ adds types to CUlaunchAttributeValue that bindgen/libclang // cannot translate, collapsing the struct to a 1-byte opaque blob while the @@ -65,14 +70,33 @@ fn run() -> Result<(), Box> { // inner union opaque produces correctly-sized byte blobs across CUDA versions. // launch_kernel_ex in cuda-core constructs this struct via raw pointer writes. .opaque_type("CUlaunchAttribute_st") - .opaque_type("CUlaunchAttributeValue_union") + .opaque_type("CUlaunchAttributeValue_union"); + for include_path in include_paths { + builder = builder.clang_arg(format!("-I{}", include_path.display())); + } + builder .generate() - .expect("Unable to generate CUDA bindings") + .map_err(|err| format!("cuda-bindings: unable to generate CUDA bindings: {err}"))? .write_to_file(Path::new(&env::var("OUT_DIR")?).join("bindings.rs"))?; Ok(()) } +/// Candidate CUDA include directories for both standard and redistributable layouts. +fn collect_include_paths(toolkit: &str) -> Vec { + let base = PathBuf::from(toolkit); + let mut paths = Vec::new(); + + paths.push(base.join("include")); + + let targets_include = base.join("targets/x86_64-linux/include"); + if targets_include.join("cuda.h").is_file() { + paths.push(targets_include); + } + + paths +} + /// Candidate directories for `rustc-link-search=native` when linking against the driver library. /// /// Adds `{toolkit}/lib64` and `{toolkit}/lib64/stubs` when `lib64` exists. If From bc64da17dadd57705be2578fd0a32f060324d67b Mon Sep 17 00:00:00 2001 From: yagna-1 Date: Wed, 27 May 2026 11:46:19 +0530 Subject: [PATCH 2/2] cuda-bindings: scan CUDA target layouts Discover CUDA headers and libraries across target-specific toolkit layouts such as targets/x86_64-linux and targets/sbsa-linux instead of hardcoding a single target directory. Signed-off-by: yagna-1 --- crates/cuda-bindings/build.rs | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/crates/cuda-bindings/build.rs b/crates/cuda-bindings/build.rs index 300eca62..494b644c 100644 --- a/crates/cuda-bindings/build.rs +++ b/crates/cuda-bindings/build.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::{env, error::Error, path::Path, path::PathBuf, process::exit}; +use std::{env, error::Error, fs, path::Path, path::PathBuf, process::exit}; /// Returns the CUDA toolkit install root: `CUDA_TOOLKIT_PATH` or `CUDA_HOME` if set, /// otherwise `/usr/local/cuda`. Used for include paths, library search paths, @@ -39,7 +39,7 @@ fn run() -> Result<(), Box> { .find(|path| path.is_file()) .ok_or_else(|| { format!( - "cuda-bindings: could not find cuda.h under {}. Set CUDA_TOOLKIT_PATH or CUDA_HOME to a CUDA Toolkit install that contains include/cuda.h or targets/x86_64-linux/include/cuda.h.", + "cuda-bindings: could not find cuda.h under {}. Set CUDA_TOOLKIT_PATH or CUDA_HOME to a CUDA Toolkit install that contains include/cuda.h or targets/*/include/cuda.h.", toolkit ) })?; @@ -88,20 +88,32 @@ fn collect_include_paths(toolkit: &str) -> Vec { let mut paths = Vec::new(); paths.push(base.join("include")); - - let targets_include = base.join("targets/x86_64-linux/include"); - if targets_include.join("cuda.h").is_file() { - paths.push(targets_include); + for target in collect_target_roots(&base) { + paths.push(target.join("include")); } paths } +/// CUDA target-layout roots such as `targets/x86_64-linux` or `targets/sbsa-linux`. +fn collect_target_roots(base: &Path) -> Vec { + let targets_dir = base.join("targets"); + let mut roots: Vec<_> = fs::read_dir(targets_dir) + .into_iter() + .flatten() + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| path.join("include/cuda.h").is_file()) + .collect(); + roots.sort(); + roots +} + /// Candidate directories for `rustc-link-search=native` when linking against the driver library. /// /// Adds `{toolkit}/lib64` and `{toolkit}/lib64/stubs` when `lib64` exists. If -/// `{toolkit}/targets/x86_64-linux/include/cuda.h` exists (redistributable / cross-layout install), -/// also adds `targets/x86_64-linux/lib` and `.../lib/stubs`. Order is preserved; duplicates are not +/// `{toolkit}/targets/*/include/cuda.h` exists (redistributable / cross-layout install), +/// also adds each matching target's `lib` and `lib/stubs`. Order is preserved; duplicates are not /// filtered. fn collect_lib_paths(toolkit: &str) -> Vec { let base = PathBuf::from(toolkit); @@ -113,10 +125,9 @@ fn collect_lib_paths(toolkit: &str) -> Vec { paths.push(lib64.join("stubs")); } - let targets = base.join("targets/x86_64-linux"); - if targets.join("include/cuda.h").is_file() { - paths.push(targets.join("lib")); - paths.push(targets.join("lib/stubs")); + for target in collect_target_roots(&base) { + paths.push(target.join("lib")); + paths.push(target.join("lib/stubs")); } paths