use std::env;
use std::path::PathBuf;
use std::process::Command;
fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("vendor");
let cuda_path = env::var("CUDA_PATH")
.or_else(|_| env::var("CUDA_HOME"))
.unwrap_or_else(|_| {
for p in &[
"/usr/local/cuda",
"/usr/local/cuda-13.2",
"/usr/local/cuda-12.6",
"/usr/local/cuda-12",
"/opt/cuda",
] {
if std::path::Path::new(p).exists() {
return p.to_string();
}
}
panic!("CUDA toolkit not found. Set CUDA_PATH or CUDA_HOME environment variable.");
});
let nvcc = format!("{}/bin/nvcc", cuda_path);
let cuda_include = format!("{}/include", cuda_path);
let cuda_lib = format!("{}/lib64", cuda_path);
let ccap = env::var("CUDA_CCAP").unwrap_or_else(|_| "120".to_string());
let blocks_per_grid = env::var("NOSTR_BLOCKS_PER_GRID").unwrap_or_else(|_| "3072".to_string());
let threads_per_block =
env::var("NOSTR_THREADS_PER_BLOCK").unwrap_or_else(|_| "256".to_string());
let keys_per_batch = env::var("KEYS_PER_THREAD_BATCH").unwrap_or_else(|_| "256".to_string());
let gpu_defines = [
format!("-DNOSTR_BLOCKS_PER_GRID={}", blocks_per_grid),
format!("-DNOSTR_THREADS_PER_BLOCK={}", threads_per_block),
format!("-DKEYS_PER_THREAD_BATCH={}", keys_per_batch),
];
let cu_files = &[
("GPU/GPURummage.cu", true), ("GPU/CudaPowMiner.cu", false), ("GPU/rummage_ffi.cu", true), ];
let mut obj_files: Vec<PathBuf> = Vec::new();
for (cu_file, needs_gpu_defines) in cu_files {
let src_path = src_dir.join(cu_file);
let obj_name = cu_file.replace('/', "_").replace(".cu", ".o");
let obj_path = out_dir.join(&obj_name);
let mut cmd = Command::new(&nvcc);
cmd.arg("-allow-unsupported-compiler")
.arg("--compile")
.args(["--compiler-options", "-fPIC"])
.arg("-O2")
.arg(format!("-I{}", src_dir.display()))
.arg(format!("-I{}", cuda_include))
.arg(format!("-gencode=arch=compute_{},code=sm_{}", ccap, ccap))
.arg("-o")
.arg(&obj_path)
.arg("-c")
.arg(&src_path);
if *needs_gpu_defines {
for d in &gpu_defines {
cmd.arg(d);
}
}
let status = cmd
.status()
.unwrap_or_else(|e| panic!("Failed to run nvcc on {}: {}", cu_file, e));
assert!(status.success(), "nvcc failed on {}", cu_file);
obj_files.push(obj_path);
}
let cpp_files = &[
"CPU/Point.cpp",
"CPU/Int.cpp",
"CPU/IntMod.cpp",
"CPU/SECP256K1.cpp",
];
let mut cc_build = cc::Build::new();
cc_build
.cpp(true)
.flag("-O2")
.flag("-march=native")
.flag("-Wno-write-strings")
.flag("-fPIC")
.define("WITHGPU", None)
.define("NOSTR_BLOCKS_PER_GRID", blocks_per_grid.as_str())
.define("NOSTR_THREADS_PER_BLOCK", threads_per_block.as_str())
.define("KEYS_PER_THREAD_BATCH", keys_per_batch.as_str())
.include(&src_dir)
.include(&cuda_include);
for f in cpp_files {
cc_build.file(src_dir.join(f));
}
cc_build.compile("rummage_cpp");
let cuda_lib_path = out_dir.join("librummage_cuda.a");
let mut ar_cmd = Command::new("ar");
ar_cmd.arg("rcs").arg(&cuda_lib_path);
for obj in &obj_files {
ar_cmd.arg(obj);
}
let status = ar_cmd.status().expect("Failed to run ar");
assert!(status.success(), "ar failed");
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=rummage_cuda");
println!("cargo:rustc-link-search=native={}", cuda_lib);
println!("cargo:rustc-link-lib=dylib=cudart");
println!("cargo:rustc-link-lib=dylib=curand");
println!("cargo:rustc-link-lib=dylib=gmp");
println!("cargo:rustc-link-lib=dylib=ssl");
println!("cargo:rustc-link-lib=dylib=crypto");
println!("cargo:rustc-link-lib=dylib=stdc++");
println!(
"cargo:rerun-if-changed={}",
src_dir.join("GPU/rummage_ffi.h").display()
);
println!(
"cargo:rerun-if-changed={}",
src_dir.join("GPU/rummage_ffi.cu").display()
);
println!(
"cargo:rerun-if-changed={}",
src_dir.join("GPU/CudaPowMiner.cu").display()
);
println!(
"cargo:rerun-if-changed={}",
src_dir.join("GPU/CudaPowMiner.h").display()
);
println!(
"cargo:rerun-if-changed={}",
src_dir.join("GPU/GPURummage.cu").display()
);
println!(
"cargo:rerun-if-changed={}",
src_dir.join("GPU/GPURummage.h").display()
);
for f in cpp_files {
println!("cargo:rerun-if-changed={}", src_dir.join(f).display());
}
let header = src_dir.join("GPU/rummage_ffi.h");
let bindings = bindgen::Builder::default()
.header(header.to_str().unwrap())
.allowlist_function("rummage_.*")
.allowlist_type("Rummage.*")
.allowlist_var("RUMMAGE_.*")
.generate_comments(true)
.derive_debug(true)
.derive_default(true)
.generate()
.expect("bindgen failed on rummage_ffi.h");
let bindings_path = out_dir.join("bindings.rs");
bindings
.write_to_file(&bindings_path)
.expect("Failed to write bindings.rs");
}