# Phase T1: Rust↔CUDA Build Chain — Design Document

> 回填文档：T1 随 commit `92acf9f` 落地（scaffold + 构建链路 + vecadd 冒烟测试），
> 当时未写文档，此处补记，供后续 Phase 回溯。

## Goal

打通「Rust 调 CUDA」的最小闭环：用 `build.rs` 调 `nvcc` 编 `csrc/*.cu`，
经手写 `extern "C"` FFI 链入 Rust；本地无 GPU/nvcc 时仍能 `cargo check`。
验收以一个 vector-add kernel 冒烟通过为准。

## Module Layout

```
xtrain/
├── Cargo.toml                  # workspace
├── csrc/
│   └── test/vecadd.cu          # 冒烟用 c[i]=a[i]+b[i]
└── crates/xtrain-cuda/
    ├── build.rs                # 检测 nvcc → 编 .cu / 链 cudart，否则发 no_cuda cfg
    └── src/
        ├── ffi.rs              # extern "C" 绑定（cudaMalloc/Memcpy/Free…+ kernel launch）
        ├── error.rs            # CudaError + check(code)
        ├── device.rs           # device_count / set_device / synchronize
        └── memory.rs           # RAII GpuBuffer（alloc/H2D/D2H/Drop）
```

## Key Design Decisions

### `build.rs` 检测 nvcc，缺失则发 `no_cuda` cfg

```rust
if !nvcc_available(&cuda_path) {
    println!("cargo:warning=nvcc not found — skipping CUDA compilation (host-only build).");
    println!("cargo:rustc-cfg=no_cuda");
    return;                       // 不编 .cu、不链 cudart
}
// 否则：cc::Build::new().cuda(true)…-gencode=arch=compute_120,code=sm_120
```

- 本地机无 GPU/nvcc → 跳过 CUDA 编译，host 侧 Rust 照常 `cargo check`。
- dash5 有 nvcc 12.9 → 实编 `.cu`，目标 `sm_120`（RTX 5090）。
- 必须配 `println!("cargo:rustc-check-cfg=cfg(no_cuda)")` 声明自定义 cfg，
  否则新版 rustc 报 `unexpected_cfgs` warning。

### `no_cuda` cfg 门控 GPU-only 代码

GPU 专属的 kernel-launch FFI 与集成测试用 `#[cfg(not(no_cuda))]` 包起来：

```rust
#[cfg(not(no_cuda))]
unsafe extern "C" {
    pub fn launch_vecadd_f32(a: *const f32, b: *const f32, c: *mut f32, n: i32, stream: CudaStream);
}
```

测试文件顶用 `#![cfg(not(no_cuda))]` 整体门控。**注意**：`cudaMalloc` 等
runtime 符号在 `ffi.rs` 里**不**门控（恒声明），本地只 `cargo check`（不链接）所以无碍；
真正链接发生在 dash5（cudart 在）。约定：**本地只 check/fmt，链接+测试都在 dash5**。

### RAII `GpuBuffer`

`alloc`/`copy_from_host`(H2D)/`copy_to_host`(D2H)，`Drop` 调 `cudaFree`，
`unsafe impl Send`。后续张量层在其上搭 device 存储（见 `docs/01-tensor.md`）。

## 验证方法

```sh
ssh dash5
export PATH=/usr/local/cuda/bin:/opt/wjh/.cargo/bin:$PATH
cd ~/projects/xtrain && cargo test -p xtrain-cuda -- --nocapture
# vecadd OK: a[i]+b[i]，c[i]=3i 通过
```

本地（无 GPU）：`cargo check` 绿（build.rs 发 `no_cuda`，GPU 测试编译出局）。

## gitea ↔ dash5 同步流

- `origin = git@gitea:gahow/xtrain.git`，分支 `main`；git 身份用 `~/.gitconfig`（不 `-c` 覆盖）。
- 本地 `git push origin main` → dash5 `cd ~/projects/xtrain && git pull` → export PATH 后构建/测试。
- 连 gitea 会打印无害的 `Welcome to VyOS` banner，可忽略。
