Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .github/workflows/examples-compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ jobs:
export LIBRARY_PATH="${sysroot}/lib:${LIBRARY_PATH:-}"
export LD_LIBRARY_PATH="${sysroot}/lib:${LD_LIBRARY_PATH:-}"

# cargo-oxide itself links libcuda (it can query the GPU arch in
# `run` mode), so its DT_NEEDED carries libcuda.so.1. The dev
# sub-packages only ship the link-time stub named libcuda.so;
# shadow it under the soname so the binary loads on a GPU-less
# runner. Compile-only never makes a real driver call. Same
# trick as unit-tests.yml.
if [ -f "${CUDA_TOOLKIT_PATH}/lib64/stubs/libcuda.so" ]; then
stub_shadow="${RUNNER_TEMP}/libcuda-stub"
mkdir -p "${stub_shadow}"
ln -sf "${CUDA_TOOLKIT_PATH}/lib64/stubs/libcuda.so" "${stub_shadow}/libcuda.so.1"
export LD_LIBRARY_PATH="${stub_shadow}:${LD_LIBRARY_PATH}"
fi

scripts/smoketest.sh --compile-only --no-color

- name: Upload failure logs
Expand Down
17 changes: 15 additions & 2 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ jobs:
# pure-Rust kernel-launch helpers + tcgen05 tiling tables
- package: cuda-host
needs_cuda: true
# cargo subcommand wrapper; transitively pulls cuda-bindings
# cargo subcommand wrapper; pure Rust, no CUDA toolkit required.
# Running it on a runner with NO toolkit installed is the
# regression gate for issue #87.
- package: cargo-oxide
needs_cuda: true
# async DeviceOperation pipeline; transitively pulls cuda-bindings
- package: cuda-async
needs_cuda: true
Expand Down Expand Up @@ -106,3 +107,15 @@ jobs:
fi

cargo test -p ${{ matrix.package }} ${{ matrix.test_args || '--all-targets' }}

# End-to-end proof for issue #87: on this runner there is no CUDA
# toolkit and no driver, yet doctor must build, load, and reach its
# checks (it exits 1 here because nvcc/cuda.h are missing, which is
# exactly the diagnosis it exists to print).
- name: cargo oxide doctor reaches its checks without the CUDA toolkit
if: matrix.package == 'cargo-oxide'
run: |
set -uo pipefail
out="$(cargo run -p cargo-oxide -- doctor 2>&1)" || true
printf '%s\n' "${out}"
printf '%s\n' "${out}" | grep -q "CUDA headers (cuda.h)"
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions crates/cargo-oxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ description = "Cargo subcommand for cuda-oxide: build and run Rust GPU programs"
name = "cargo-oxide"
path = "src/main.rs"

# Deliberately CUDA-free: no dependency here may pull `cuda-bindings`
# (or any other build script that needs the CUDA toolkit), so that
# `cargo oxide doctor` builds and runs on a machine with no toolkit and
# no driver. libnvvm-sys / nvjitlink-sys are pure-Rust runtime-dlopen
# crates and are fine.
[dependencies]
clap = { version = "4", features = ["derive"] }
libnvvm-sys = { workspace = true }
nvjitlink-sys = { workspace = true }
cuda-host = { workspace = true }
cuda-core = { workspace = true }
toml = "1"
12 changes: 11 additions & 1 deletion crates/cargo-oxide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,17 @@ Formats all crates in the workspace: root workspace, `rustc-codegen-cuda`, and a

### `cargo oxide doctor`

Validates that your environment is correctly set up: Rust nightly toolchain, CUDA toolkit (`nvcc`), LLVM (`llc`), and the codegen backend `.so`.
Validates that your environment is correctly set up: Rust nightly toolchain,
CUDA headers (`cuda.h`), CUDA toolkit (`nvcc`, libNVVM, nvJitLink,
libdevice), LLVM (`llc`), clang/libclang, the NVIDIA driver / GPU, and the
codegen backend `.so`. Every check reports what was found or how to fix it.

`cargo-oxide` itself builds and runs without the CUDA toolkit and without an
NVIDIA driver, and `doctor` never builds anything first, so it works on a
bare machine and tells you exactly what is missing. The driver / GPU check is
informational (only `cargo oxide run` needs a GPU), and a missing backend
`.so` just points at `cargo oxide setup` (`run`/`build` build it on demand
anyway).

### `cargo oxide setup`

Expand Down
28 changes: 28 additions & 0 deletions crates/cargo-oxide/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,34 @@ pub fn find_or_build_backend(workspace_root: &Path) -> PathBuf {
auto_fetch_and_build()
}

/// Returns where the backend `.so` lives (or would live), with NO side
/// effects: never builds, never clones, never touches the network.
///
/// Mirrors the discovery order of [`find_or_build_backend`] minus its
/// build/clone steps:
///
/// 1. `CUDA_OXIDE_BACKEND` env var, returned even when the file is missing
/// so the caller can report the configured-but-absent path.
/// 2. Local repo build path (`crates/rustc-codegen-cuda/target/debug/...`).
/// 3. Cache path at `~/.cargo/cuda-oxide/librustc_codegen_cuda.so`.
///
/// `cargo oxide doctor` uses this so that a diagnostic run never triggers a
/// multi-minute backend build or a git clone before it can print anything.
pub fn backend_so_candidate(workspace_root: &Path) -> PathBuf {
if let Ok(path) = std::env::var("CUDA_OXIDE_BACKEND") {
return PathBuf::from(path);
}

let codegen_crate = workspace_root.join("crates/rustc-codegen-cuda");
if codegen_crate.is_dir() {
return codegen_crate.join("target/debug/librustc_codegen_cuda.so");
}

cache_directory()
.map(|dir| dir.join("librustc_codegen_cuda.so"))
.unwrap_or_else(|| PathBuf::from("librustc_codegen_cuda.so"))
}

/// Returns true when the cached backend `.so` is older than the running
/// `cargo-oxide` binary, which means the user has upgraded the binary
/// since the cache was last built.
Expand Down
Loading
Loading