yum/vfft

git clone https://git.yummers.dev/yum/vfft

yumBuild script now uses OUT_DIR4c5f4de

master
4.1 KiB134 linesraw
1use anyhow::Context;
2use std::fs;
3use std::fs::File;
4use std::fs::OpenOptions;
5use std::io::Write;
6use std::path::{Path, PathBuf};
7use std::process::{Command, Output};
8
9fn say(message: &str) {
10    let file = OpenOptions::new().write(true).open("/dev/tty");
11    if file.is_ok() {
12        let _ = writeln!(file.unwrap(), "gpu_fft: {message}");
13    }
14}
15
16fn run_cmd(cmd: &str, dir: &Path) -> anyhow::Result<Output> {
17    println!("cargo::warning=Running {cmd}...");
18    let output = Command::new("sh")
19        .arg("-c")
20        .arg(cmd)
21        .current_dir(dir)
22        .output()
23        .with_context(|| format!("failed to execute `{cmd}`"))?;
24
25    if !output.status.success() {
26        anyhow::bail!(
27            "Command failed `{cmd}`:\nstdout:{}\nstderr:\n{}",
28            String::from_utf8_lossy(&output.stdout),
29            String::from_utf8_lossy(&output.stderr)
30        );
31    }
32
33    Ok(output)
34}
35
36fn ensure_slang(slang_dir: &Path) -> Result<PathBuf, anyhow::Error> {
37    let sentinel = slang_dir.join(".did_build");
38    let slangc = slang_dir.join("build/RelWithDebInfo/bin/slangc");
39    if sentinel.exists() {
40        // Already built - short-circuit.
41        return Ok(slangc);
42    }
43
44    say(concat!(
45        "The slang compiler is not present, or is missing the\n",
46        "sentinel file. We need the slang compiler in order to translate\n",
47        ".slang shaders into .wgsl, which wgpu (webGPU) uses. We're going to\n",
48        "acquire the slang compiler, then build it. This will take a few\n",
49        "minutes and use a lot of CPU. This only needs to run once."
50    ));
51
52    // Assume we're in some weird intermediate state and start fresh.
53    if slang_dir.exists() {
54        say("Removing old slang compiler...");
55        fs::remove_dir_all(slang_dir)?;
56    }
57
58    say("cargo::warning=Acquiring slang compiler...");
59    let clone_ret = Command::new("git")
60        .args([
61            "clone",
62            "--recursive",
63            "https://github.com/shader-slang/slang",
64        ])
65        .arg(slang_dir)
66        .status()?;
67    anyhow::ensure!(clone_ret.success(), "Failed to clone slang");
68    run_cmd("mkdir build", slang_dir)?;
69
70    say("Building slang compiler...");
71    run_cmd("cmake -B build --preset default", slang_dir)?;
72    run_cmd("cmake --build --preset releaseWithDebugInfo", slang_dir)?;
73    File::create(sentinel)?;
74    Ok(slangc)
75}
76
77// Convert slang shaders in shaders/ to wgsl. (I refuse to use the term "transpile" - it is
78// redundant and ignores the fact that all compilation is merely a translation from one language to
79// another.)
80fn compile_shaders(
81    slangc: &PathBuf,
82    slang_dir: &Path,
83    wgsl_dir: &PathBuf,
84) -> Result<(), anyhow::Error> {
85    if Path::new(wgsl_dir).exists() {
86        fs::remove_dir_all(wgsl_dir)?;
87    }
88    fs::create_dir_all(wgsl_dir)
89        .with_context(|| format!("Failed to create output directory {wgsl_dir:?}"))?;
90
91    for entry in fs::read_dir(slang_dir)? {
92        let entry = entry?.path();
93        let Some(ext) = entry.extension() else {
94            // No extension.
95            continue;
96        };
97        if ext != "slang" {
98            continue;
99        }
100        let stem = entry
101            .file_stem()
102            .with_context(|| format!("Failed to get file stem for {}", entry.display()))?;
103        let new_path = Path::new(wgsl_dir).join(stem).with_extension("wgsl");
104        let slang_dir = entry.parent().ok_or(anyhow::anyhow!(
105            "Failed to get parent directory for {}",
106            entry.display()
107        ))?;
108        let compile_ret = Command::new(slangc)
109            .arg(&entry)
110            .arg("-o")
111            .arg(&new_path)
112            .arg("-no-mangle")
113            .arg("-I")
114            .arg(slang_dir)
115            .status()?;
116        anyhow::ensure!(compile_ret.success(), "Failed to compile shader");
117    }
118
119    Ok(())
120}
121
122fn main() -> anyhow::Result<()> {
123    if std::env::var_os("CARGO_FEATURE_GPU").is_none() {
124        return Ok(());
125    }
126
127    let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
128    let slangc = ensure_slang(&out.join("slang"))?;
129
130    println!("cargo:rerun-if-changed=shaders");
131    compile_shaders(&slangc, Path::new("./shaders"), &out.join("shaders"))?;
132
133    Ok(())
134}