mircuda-sys 0.1.0

Private native CUDA integration for mircuda
use std::{env, error::Error, path::PathBuf};

fn main() -> Result<(), Box<dyn Error>> {
    println!("cargo::rerun-if-env-changed=MIRCUDA_CUTLASS_DIR");
    println!("cargo::rerun-if-env-changed=MIRCUDA_CUDA_ARCH");
    println!("cargo::rerun-if-env-changed=CUDA_HOME");
    println!("cargo::rerun-if-changed=native/cutlass_probe.cu");
    println!("cargo::rerun-if-changed=native/dense_sm120.cu");
    println!("cargo::rerun-if-changed=native/dense_sm120.cuh");
    println!("cargo::rerun-if-changed=native/dense_vector_sm120.cu");
    println!("cargo::rerun-if-changed=native/dense_vector_sm120.cuh");
    println!("cargo::rerun-if-changed=native/fp4_sm120.cu");
    println!("cargo::rerun-if-changed=native/fp4_vector_sm120.cu");
    println!("cargo::rerun-if-changed=native/fp8_vector_sm120.cu");
    println!("cargo::rerun-if-changed=native/grouped_fp4_sm120.cu");
    println!("cargo::rerun-if-changed=native/grouped_fp4_sm120.cuh");
    println!("cargo::rerun-if-changed=native/variable_grouped_fp4_sm120.cu");
    println!("cargo::rerun-if-changed=native/variable_grouped_fp4_sm120.cuh");
    if env::var_os("CARGO_FEATURE_CUTLASS").is_none()
        || env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("linux")
    {
        return Ok(());
    }
    let cutlass = cutlass_dir()?;
    let include = cutlass.join("include");
    let util = cutlass.join("tools/util/include");
    if !include.join("cutlass/cutlass.h").is_file() {
        return Err(format!("CUTLASS 4.4.2 headers not found in {}", cutlass.display()).into());
    }
    let cuda =
        env::var_os("CUDA_HOME").map_or_else(|| PathBuf::from("/usr/local/cuda"), PathBuf::from);
    let arch = cuda_arch()?;
    let gencode = format!("-gencode=arch=compute_{arch},code=sm_{arch}");
    let configure = |build: &mut cc::Build| {
        build
            .cuda(true)
            .debug(false)
            .opt_level(3)
            .warnings(false)
            .cudart("shared")
            .include(&include)
            .include(&util)
            .include(cuda.join("include"))
            .flag("-std=c++17")
            .flag("--expt-relaxed-constexpr")
            .flag("-diag-suppress=20013")
            .flag("-diag-suppress=20015")
            .flag("-diag-suppress=2908")
            .flag(&gencode);
    };
    let mut probe = cc::Build::new();
    configure(&mut probe);
    probe.file("native/cutlass_probe.cu").compile("mircuda_cutlass_probe");
    let mut dense = cc::Build::new();
    configure(&mut dense);
    dense.file("native/dense_sm120.cu").compile("mircuda_cutlass_dense");
    let mut dense_vector = cc::Build::new();
    configure(&mut dense_vector);
    dense_vector
        .file("native/dense_vector_sm120.cu")
        .compile("mircuda_cutlass_dense_vector");
    let mut fp4 = cc::Build::new();
    configure(&mut fp4);
    fp4.file("native/fp4_sm120.cu").compile("mircuda_cutlass_fp4");
    let mut fp4_vector = cc::Build::new();
    configure(&mut fp4_vector);
    fp4_vector
        .file("native/fp4_vector_sm120.cu")
        .compile("mircuda_cutlass_fp4_vector");
    let mut fp8_vector = cc::Build::new();
    configure(&mut fp8_vector);
    fp8_vector
        .file("native/fp8_vector_sm120.cu")
        .compile("mircuda_cutlass_fp8_vector");
    let mut grouped_fp4 = cc::Build::new();
    configure(&mut grouped_fp4);
    grouped_fp4
        .file("native/grouped_fp4_sm120.cu")
        .compile("mircuda_cutlass_grouped_fp4");
    let mut variable_grouped_fp4 = cc::Build::new();
    configure(&mut variable_grouped_fp4);
    variable_grouped_fp4
        .file("native/variable_grouped_fp4_sm120.cu")
        .compile("mircuda_cutlass_variable_grouped_fp4");
    Ok(())
}

fn cuda_arch() -> Result<String, Box<dyn Error>> {
    let arch = env::var("MIRCUDA_CUDA_ARCH").unwrap_or_else(|_| String::from("120f"));
    if arch.chars().all(|character| character.is_ascii_digit())
        || (arch.ends_with(['a', 'f'])
            && arch[..arch.len() - 1].chars().all(|character| character.is_ascii_digit()))
    {
        Ok(arch)
    } else {
        Err(format!("invalid MIRCUDA_CUDA_ARCH: {arch}").into())
    }
}

fn cutlass_dir() -> Result<PathBuf, Box<dyn Error>> {
    if let Some(path) = env::var_os("MIRCUDA_CUTLASS_DIR") {
        return Ok(PathBuf::from(path));
    }
    let home = env::var_os("HOME").ok_or("HOME is unavailable")?;
    Ok(PathBuf::from(home).join(".cache/mircuda/cutlass-v4.4.2"))
}