decuda 0.1.1

CUDA to HIP, SYCL, OpenCL, and Rust GPU migration tool — automatic source-code translator for porting CUDA C++ kernels to AMD ROCm HIP, Intel oneAPI SYCL, Khronos OpenCL, and Rust GPU (cust / rust-gpu)
Documentation
//! CLI surface for decuda.

#![allow(dead_code)]

use std::path::PathBuf;

use anyhow::Result;
use clap::{Parser, Subcommand, ValueEnum};

use crate::cuda_db;
use crate::migrate;
use crate::migrate::MigrateOptions;
use crate::parser;
use crate::walker;

/// Migrate CUDA source code to other GPU programming languages.
#[derive(Debug, Parser)]
#[command(name = "decuda", version, about, long_about = None)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Command,
}

#[derive(Debug, Subcommand)]
pub enum Command {
    /// Translate CUDA files to one or more targets.
    Migrate {
        /// Input file or directory containing .cu / .cuh files.
        #[arg(short, long)]
        input: PathBuf,

        /// Output directory. Each target writes into a `<target>/` subfolder.
        #[arg(short, long)]
        output: PathBuf,

        /// Which backend to emit. Use `all` to emit every supported backend.
        #[arg(short, long, value_enum, default_value_t = Target::All)]
        target: Target,

        /// Parse and plan only; do not write output.
        #[arg(long)]
        dry_run: bool,

        /// Emit progress to stderr.
        #[arg(short, long)]
        verbose: bool,

        /// Only process files matching this name (substring match).
        #[arg(long)]
        filter: Option<String>,
    },
    /// Inspect a file and print the IR nodes found (for debugging).
    Inspect {
        /// Input file or directory.
        #[arg(short, long)]
        input: PathBuf,
        /// When `--input` is a directory, restrict to files matching this name.
        #[arg(long)]
        filter: Option<String>,
    },
    /// List all CUDA APIs known to the database.
    ListApis {
        /// Restrict to one target backend.
        #[arg(short, long, value_enum)]
        target: Option<Target>,
    },
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
pub enum Target {
    Hip,
    Sycl,
    Rust,
    Opencl,
    All,
}

impl Target {
    pub fn as_str(&self) -> &'static str {
        match self {
            Target::Hip => "hip",
            Target::Sycl => "sycl",
            Target::Rust => "rust",
            Target::Opencl => "opencl",
            Target::All => "all",
        }
    }

    pub fn iter_real() -> [Target; 4] {
        [Target::Hip, Target::Sycl, Target::Rust, Target::Opencl]
    }
}

pub fn run() -> Result<()> {
    let cli = Cli::parse();
    match cli.command {
        Command::Migrate {
            input,
            output,
            target,
            dry_run,
            verbose,
            filter,
        } => {
            let opts = MigrateOptions {
                input,
                output,
                target,
                dry_run,
                verbose,
                filter,
            };
            let report = migrate::run(opts)?;
            crate::report::write_human_report(&report);
            if report.has_errors() {
                anyhow::bail!("migration completed with errors; see report");
            }
            Ok(())
        }
        Command::Inspect { input, filter } => {
            let files = walker::collect_cuda_files(&input, filter.as_deref())?;
            for path in files {
                println!("==> {}", path.display());
                let unit = parser::translate_path(&path)?;
                unit.print_summary();
            }
            Ok(())
        }
        Command::ListApis { target } => {
            let db = cuda_db::database();
            let entries: std::collections::BTreeMap<_, _> = db.iter().collect();
            for (cuda_name, info) in &entries {
                if let Some(t) = target {
                    if !info.supports(t) {
                        continue;
                    }
                }
                println!("{:<36} {}", cuda_name, info.summarize(target.unwrap_or(Target::All)));
            }
            Ok(())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn iter_real_has_four_backends() {
        assert_eq!(Target::iter_real().len(), 4);
    }

    #[test]
    fn target_str_unique() {
        let s: std::collections::HashSet<_> =
            Target::iter_real().iter().map(|t| t.as_str()).collect();
        assert_eq!(s.len(), 4);
    }
}