From 1d61f8a467f781bbef88f043fa2009c36d5a0841 Mon Sep 17 00:00:00 2001 From: Alejandro Cabeza Romero Date: Wed, 10 Jun 2026 12:28:47 +0200 Subject: [PATCH] Single source of truth for rapidsnark version in RAPIDSNARK_VERSION. --- RAPIDSNARK_VERSION | 1 + crates/build.rs | 88 ++++++++++++++++++++++++----------- crates/download_rapidsnark.sh | 8 +++- 3 files changed, 69 insertions(+), 28 deletions(-) create mode 100644 RAPIDSNARK_VERSION diff --git a/RAPIDSNARK_VERSION b/RAPIDSNARK_VERSION new file mode 100644 index 0000000..d169b2f --- /dev/null +++ b/RAPIDSNARK_VERSION @@ -0,0 +1 @@ +0.0.8 diff --git a/crates/build.rs b/crates/build.rs index 39819cc..e01e711 100644 --- a/crates/build.rs +++ b/crates/build.rs @@ -4,40 +4,30 @@ use std::path::Path; use std::process::Command; const RAPIDSNARK_DOWNLOAD_SCRIPT: &str = include_str!("./download_rapidsnark.sh"); +const RAPIDSNARK_LIB_DIR_ENV_VAR: &str = "RAPIDSNARK_LIB_DIR"; +const RAPIDSNARK_VERSION_FILE: &str = "../RAPIDSNARK_VERSION"; fn main() { let target = env::var("TARGET").unwrap(); - let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set"); + let out_dir = env::var("OUT_DIR").unwrap(); let arch = target.split('-').next().unwrap(); // See: https://github.com/zkmopro/chkstk_stub chkstk_stub::build(); - // Try to list contents of the target directory - let rapidsnark_path = Path::new(&out_dir).join(Path::new("rapidsnark")); - // If the rapidsnark repo is not downloaded, download it - if !rapidsnark_path.exists() { - let rapidsnark_script_path = Path::new(&out_dir).join(Path::new("download_rapidsnark.sh")); - fs::write(&rapidsnark_script_path, RAPIDSNARK_DOWNLOAD_SCRIPT) - .expect("Failed to write build script"); - let child_process = Command::new("sh") - .arg(rapidsnark_script_path.to_str().unwrap()) - .spawn(); - if let Err(e) = child_process { - panic!("Failed to spawn rapidsnark download: {e}"); - } - let status = child_process.unwrap().wait(); - if let Err(e) = status { - panic!("Failed to wait for rapidsnark download: {e}"); - } else if !status.unwrap().success() { - panic!("Failed to wait for rapidsnark download"); - } - } - let absolute_lib_path = if rapidsnark_path.join(&target).exists() { - rapidsnark_path.join(&target) + println!("cargo:rerun-if-changed={RAPIDSNARK_VERSION_FILE}"); + println!("cargo:rerun-if-env-changed={RAPIDSNARK_LIB_DIR_ENV_VAR}"); + + let rapidsnark_lib_dir = if let Ok(lib_dir) = env::var(RAPIDSNARK_LIB_DIR_ENV_VAR) { + println!("cargo:warning=rapidsnark: using pre-supplied libs from {RAPIDSNARK_LIB_DIR_ENV_VAR}={lib_dir}"); + Path::new(&lib_dir).to_path_buf() } else { - rapidsnark_path.join(arch) - }; + println!("cargo:warning=rapidsnark: {RAPIDSNARK_LIB_DIR_ENV_VAR} not set, downloading..."); + provide_rapidsnark(&out_dir, arch) + } + .canonicalize() + .unwrap_or_else(|error| panic!("{RAPIDSNARK_LIB_DIR_ENV_VAR} is not a valid path: {error}")); + let compiler = cc::Build::new().get_compiler(); let cpp_stdlib = if compiler.is_like_clang() { "c++" @@ -47,7 +37,7 @@ fn main() { println!( "cargo:rustc-link-search=native={}", - absolute_lib_path.display() + rapidsnark_lib_dir.display() ); // The shared rapidsnark artifact is already linked with its fr/fq/gmp implementation. @@ -73,6 +63,52 @@ fn main() { println!("cargo:rustc-link-lib={thread_lib}"); } +fn rapidsnark_version() -> String { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); + let version_file = Path::new(&manifest_dir).join(RAPIDSNARK_VERSION_FILE); + fs::read_to_string(&version_file) + .unwrap_or_else(|e| panic!("Failed to read {}: {e}", version_file.display())) + .trim() + .to_string() +} + +fn provide_rapidsnark(out_dir: &str, arch: &str) -> std::path::PathBuf { + let rapidsnark_path = Path::new(out_dir).join("rapidsnark"); + if !is_rapidsnark_downloaded(&rapidsnark_path, arch) { + let version = rapidsnark_version(); + let rapidsnark_script_path = Path::new(out_dir).join("download_rapidsnark.sh"); + fs::write(&rapidsnark_script_path, RAPIDSNARK_DOWNLOAD_SCRIPT) + .expect("Failed to write build script"); + let child_process = Command::new("sh") + .arg(rapidsnark_script_path.to_str().unwrap()) + .env("RAPIDSNARK_VERSION", &version) + .spawn(); + if let Err(e) = child_process { + panic!("Failed to spawn rapidsnark download: {e}"); + } + let status = child_process.unwrap().wait(); + if let Err(e) = status { + panic!("Failed to wait for rapidsnark download: {e}"); + } else if !status.unwrap().success() { + panic!("Failed to wait for rapidsnark download"); + } + } + rapidsnark_path.join(arch) +} + +/// Checks whether the arch-specific subdirectory exists, not just the parent directory. +/// +/// The download script creates the parent via `mkdir -p` before fetching, so checking the parent +/// would silently skip re-downloads after a previously interrupted build. +/// +/// The `arch` subdirectory (e.g. `x86_64/`) is itself unnecessary for isolation: `OUT_DIR` is +/// already scoped to the full target triple by Cargo. A cleaner future refactor (option B) would +/// have the script copy libs directly into `$OUT_DIR/rapidsnark/lib/` and sentinel on the presence +/// of `librapidsnark.a` — eliminating the extra directory level. +fn is_rapidsnark_downloaded(rapidsnark_path: &Path, arch: &str) -> bool { + rapidsnark_path.join(arch).exists() +} + fn is_static_rapidsnark() -> bool { env::var_os("CARGO_FEATURE_STATIC_RAPIDSNARK").is_some() } diff --git a/crates/download_rapidsnark.sh b/crates/download_rapidsnark.sh index 6c32f17..ec03d82 100755 --- a/crates/download_rapidsnark.sh +++ b/crates/download_rapidsnark.sh @@ -14,8 +14,12 @@ if [ -z "$TARGET" ]; then exit 1 fi -# Pinned rapidsnark release. Bump this to update the prebuilt artifacts. -VERSION="v0.0.8" +# Passed by build.rs from RAPIDSNARK_VERSION at the repo root. See CONTRIBUTING.md for bump instructions. +if [ -z "$RAPIDSNARK_VERSION" ]; then + echo "RAPIDSNARK_VERSION not specified" + exit 1 +fi +VERSION="v${RAPIDSNARK_VERSION}" # Upstream iden3 prebuilt archives (used for macOS / iOS / Android). IDEN3_BASE="https://github.com/iden3/rapidsnark/releases/download/$VERSION" # The iden3 Linux archives are non-PIC and built against a newer glibc, so they