use std::fs::File; use std::path::{Path, PathBuf}; use std::process::Command; use std::{env, fs, io}; use cargo_metadata::MetadataCommand; use zip::ZipArchive; fn main() { commit_info(); let rev = cairo_version(); download_core(&rev); } fn is_docs_rs() -> bool { env::var("DOCS_RS").is_ok() } fn commit_info() { if !Path::new("../.git").exists() { return; } println!("cargo:rerun-if-changed=../.git/index"); let output = match Command::new("git") .arg("log") .arg("-1") .arg("--date=short") .arg("--format=%H %h %cd") .arg("--abbrev=9") .current_dir("..") .output() { Ok(output) if output.status.success() => output, _ => return, }; let stdout = String::from_utf8(output.stdout).unwrap(); let mut parts = stdout.split_whitespace(); let mut next = || parts.next().unwrap(); println!("cargo:rustc-env=SCARB_COMMIT_HASH={}", next()); println!("cargo:rustc-env=SCARB_COMMIT_SHORT_HASH={}", next()); println!("cargo:rustc-env=SCARB_COMMIT_DATE={}", next()) } fn cairo_version() -> String { let cargo_lock = find_cargo_lock(); println!("cargo:rerun-if-changed={}", cargo_lock.display()); let metadata = MetadataCommand::new() .manifest_path(Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml")) .verbose(true) .exec() .expect("Failed to execute cargo metadata"); let resolve = metadata .resolve .expect("Expected metadata resolve to be present."); let root = resolve .root .expect("Expected metadata resolve root to be present."); assert!( root.repr.starts_with("scarb "), "Expected metadata resolve root to be `scarb`." ); let scarb_node = resolve.nodes.iter().find(|node| node.id == root).unwrap(); let compiler_dep = scarb_node .deps .iter() .find(|dep| dep.name == "cairo_lang_compiler") .unwrap(); let compiler_package = metadata .packages .iter() .find(|pkg| pkg.id == compiler_dep.pkg) .unwrap(); let version = compiler_package.version.to_string(); println!("cargo:rustc-env=SCARB_CAIRO_VERSION={version}"); if let Some(source) = &compiler_package.source { let source = source.to_string(); if source.starts_with("git+") { if let Some((_, commit)) = source.split_once('#') { println!("cargo:rustc-env=SCARB_CAIRO_COMMIT_HASH={commit}"); return commit.to_string(); } } } format!("refs/tags/v{version}") } fn download_core(rev: &str) { println!("cargo:rerun-if-env-changed=CAIRO_ARCHIVE"); let out_dir = env::var("OUT_DIR").unwrap(); if is_docs_rs() { eprintln!("Docs.rs build detected. Skipping corelib download."); let core_stub_path = PathBuf::from_iter([&out_dir, "core-stub"]); fs::create_dir_all(&core_stub_path).unwrap(); println!( "cargo:rustc-env=SCARB_CORE_PATH={}", core_stub_path.display() ); return; } let core_path = PathBuf::from_iter([&out_dir, &format!("core-{}", ident(rev))]); if !core_path.is_dir() { let cairo_zip = PathBuf::from_iter([&out_dir, "cairo.zip"]); if let Ok(cairo_archive) = std::env::var("CAIRO_ARCHIVE") { // Copy archive to `cairo_zip`, without keeping file attributes. eprintln!("Copying Cairo archive from `CAIRO_ARCHIVE={cairo_archive}`."); let mut src = File::open(&cairo_archive).unwrap(); let mut dst = File::create(&cairo_zip).unwrap(); io::copy(&mut src, &mut dst).unwrap(); } else { let url = format!("https://github.com/starkware-libs/cairo/archive/{rev}.zip"); let mut curl = Command::new("curl"); curl.args(["--proto", "=https", "--tlsv1.2", "-fL"]); curl.arg("-o"); curl.arg(&cairo_zip); curl.arg(&url); eprintln!("{curl:?}"); let curl_exit = curl.status().expect("Failed to start curl"); if !curl_exit.success() { panic!("Failed to download {url} with curl") } } fs::create_dir_all(&core_path).unwrap(); let cairo_file = File::open(cairo_zip).unwrap(); let mut cairo_archive = ZipArchive::new(cairo_file).unwrap(); for i in 0..cairo_archive.len() { let mut input = cairo_archive.by_index(i).unwrap(); if input.name().ends_with('/') { continue; } let path = input.enclosed_name().unwrap(); let path = PathBuf::from_iter(path.components().skip(1)); let Ok(path) = path.strip_prefix("corelib") else { continue; }; let path = core_path.join(path); if let Some(parent) = path.parent() { fs::create_dir_all(parent).unwrap(); } let mut output = File::create(path).unwrap(); io::copy(&mut input, &mut output).unwrap(); } } println!("cargo:rustc-env=SCARB_CORE_PATH={}", core_path.display()); } fn ident(id: &str) -> String { let mut ident = String::with_capacity(id.len()); for ch in id.chars() { ident.push(if ch.is_ascii_alphanumeric() { ch } else { '_' }) } ident } fn find_cargo_lock() -> PathBuf { let in_workspace = PathBuf::from("../Cargo.lock"); if in_workspace.exists() { return in_workspace; } let in_package = PathBuf::from("Cargo.lock"); if in_package.exists() { return in_package; } panic!( "Couldn't find Cargo.lock of this package. \ Something's wrong with build execution environment." ) }