run_clang_tidy/
run_clang_tidy.rs

1use std::{
2    path::{Path, PathBuf},
3    process::Command,
4};
5
6use clap::{arg, Parser};
7use openvm_scripts::{
8    find_cuda_include_dirs, find_files_with_extension, get_cuda_dep_common_include_dirs,
9};
10
11#[derive(Parser)]
12#[command(author, version, about = "Run Clang-Tidy on CUDA files")]
13pub struct Args {
14    /// Root directory to search
15    directory: PathBuf,
16
17    /// Path to clang-tidy (default: clang-tidy from PATH)
18    #[arg(long, default_value = "clang-tidy")]
19    clang_tidy: String,
20
21    /// Path to CUDA installation
22    #[arg(long, default_value = "/usr/local/cuda")]
23    cuda_path: String,
24
25    /// CUDA architecture to use
26    #[arg(long, default_value = "80")]
27    cuda_arch: String,
28}
29
30fn run_clang_tidy_single(
31    file: &Path,
32    clang_tidy: &str,
33    cuda_path: &str,
34    cuda_arch: &str,
35    includes: &[String],
36) -> eyre::Result<bool> {
37    let mut cmd = Command::new(clang_tidy);
38
39    cmd.args([
40        "--checks='*'",
41        "--warnings-as-errors='*'",
42        "--header-filter='.*'",
43        "--extra-arg=-Wno-unknown-cuda-version",
44    ])
45    .arg(file.to_str().unwrap())
46    .args(["--", "-x", "cuda", "-std=c++17"])
47    .arg(format!("--cuda-path={cuda_path}"))
48    .arg(format!("--cuda-gpu-arch=sm_{cuda_arch}"))
49    .arg("-D__CUDACC__")
50    .arg(format!("-I{cuda_path}/include"));
51
52    // Check if CCCL directory exists (CUDA 13.0+)
53    let cccl_path = PathBuf::from(cuda_path).join("include").join("cccl");
54    if cccl_path.exists() {
55        cmd.arg(format!("-I{}", cccl_path.display()));
56    }
57
58    for include in includes {
59        cmd.arg(format!("-I{include}"));
60    }
61
62    println!("Running for {}", file.display());
63
64    let status = cmd.status()?;
65    Ok(status.success())
66}
67
68fn main() -> eyre::Result<()> {
69    let args = Args::parse();
70    let include_dirs = find_cuda_include_dirs(&args.directory)
71        .iter()
72        .chain(get_cuda_dep_common_include_dirs().iter())
73        .map(|p| p.to_string_lossy().to_string())
74        .collect::<Vec<_>>();
75    let files = find_files_with_extension(&args.directory, "cu");
76
77    if files.is_empty() {
78        println!("No .cu files found.");
79        return Ok(());
80    }
81
82    let mut failures = Vec::new();
83
84    for file in &files {
85        match run_clang_tidy_single(
86            file,
87            &args.clang_tidy,
88            &args.cuda_path,
89            &args.cuda_arch,
90            &include_dirs,
91        ) {
92            Ok(true) => {}
93            Ok(false) => failures.push(file),
94            Err(e) => {
95                eprintln!("Error running clang-tidy on {}: {}", file.display(), e);
96                failures.push(file);
97            }
98        }
99    }
100
101    println!("\n=== Summary ===");
102    println!(
103        "Total: {}  Succeeded: {}  Failed: {}",
104        files.len(),
105        files.len() - failures.len(),
106        failures.len()
107    );
108
109    if !failures.is_empty() {
110        println!("Failed files:");
111        for file in failures {
112            println!("  {}", file.display());
113        }
114        return Err(eyre::eyre!("Failed files found"));
115    }
116    Ok(())
117}