From a835754d4106d29553ca07b9043e271e5f181aae Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Tue, 25 Mar 2025 11:20:00 +0100 Subject: [PATCH] check the supported solc version Signed-off-by: Cyrill Leutwiler --- Cargo.lock | 2 +- crates/core/src/driver/mod.rs | 8 +- crates/format/src/mode.rs | 21 +++++ crates/solc-binaries/Cargo.toml | 2 +- crates/solc-binaries/src/cache.rs | 112 ++++++++++++--------------- crates/solc-binaries/src/download.rs | 11 ++- crates/solc-binaries/src/lib.rs | 4 +- 7 files changed, 87 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c6e83e..54c817b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2937,7 +2937,7 @@ version = "0.1.0" dependencies = [ "anyhow", "hex", - "once_cell", + "log", "reqwest", "semver 1.0.26", "serde", diff --git a/crates/core/src/driver/mod.rs b/crates/core/src/driver/mod.rs index 1756bd6..4344378 100644 --- a/crates/core/src/driver/mod.rs +++ b/crates/core/src/driver/mod.rs @@ -11,7 +11,6 @@ use revive_dt_format::{ use revive_dt_node::Node; use revive_dt_solc_binaries::download_solc; use revive_solc_json_interface::SolcStandardJsonOutput; -use semver::Version; use crate::Platform; @@ -41,19 +40,22 @@ where } pub fn build_contracts(&mut self, mode: &SolcMode, metadata: &Metadata) -> anyhow::Result<()> { + let Some(version) = mode.last_patch_version(&self.config.solc) else { + anyhow::bail!("unsupported solc version: {:?}", mode.solc_version); + }; + let sources = metadata.contract_sources()?; let base_path = metadata.directory()?.display().to_string(); - let mut compiler = Compiler::::new().base_path(base_path.clone()); for (file, _contract) in sources.values() { compiler = compiler.with_source(file)?; } - let version = Version::new(0, 8, 29); let solc_path = download_solc(self.config.directory(), version, self.config.wasm)?; let output = compiler .solc_optimizer(mode.solc_optimize()) .try_build(solc_path)?; + self.contracts.insert(output.input, output.output); Ok(()) diff --git a/crates/format/src/mode.rs b/crates/format/src/mode.rs index 6f58ac0..638bf67 100644 --- a/crates/format/src/mode.rs +++ b/crates/format/src/mode.rs @@ -1,3 +1,4 @@ +use semver::Version; use serde::Deserialize; use serde::de::Deserializer; @@ -57,6 +58,26 @@ impl SolcMode { pub fn solc_optimize(&self) -> bool { self.solc_optimize.unwrap_or(true) } + + /// Calculate the latest matching solc patch version. Returns: + /// - `latest_supported` if no version request was specified. + /// - A matching version with the same minor version as `latest_supported`, if any. + /// - `None` if no minor version of the `latest_supported` version matches. + pub fn last_patch_version(&self, latest_supported: &Version) -> Option { + let Some(version_req) = self.solc_version.as_ref() else { + return Some(latest_supported.to_owned()); + }; + + // lgtm + for patch in (0..latest_supported.patch + 1).rev() { + let version = Version::new(0, latest_supported.minor, patch); + if version_req.matches(&version) { + return Some(version); + } + } + + None + } } impl<'de> Deserialize<'de> for Mode { diff --git a/crates/solc-binaries/Cargo.toml b/crates/solc-binaries/Cargo.toml index 13351a3..e694d3b 100644 --- a/crates/solc-binaries/Cargo.toml +++ b/crates/solc-binaries/Cargo.toml @@ -11,7 +11,7 @@ rust-version.workspace = true [dependencies] anyhow = { workspace = true } hex = { workspace = true } -once_cell = { workspace = true } +log = { workspace = true } reqwest = { workspace = true } semver = { workspace = true } serde = { workspace = true } diff --git a/crates/solc-binaries/src/cache.rs b/crates/solc-binaries/src/cache.rs index acc8a90..ee664e9 100644 --- a/crates/solc-binaries/src/cache.rs +++ b/crates/solc-binaries/src/cache.rs @@ -1,84 +1,70 @@ //! Helper for caching the solc binaries. use std::{ - cell::OnceCell, collections::HashSet, fs::{File, create_dir_all}, - io::Write, + io::{BufWriter, Write}, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, - sync::Mutex, + sync::{LazyLock, Mutex}, }; use crate::download::GHDownloader; pub const SOLC_CACHE_DIRECTORY: &str = "solc"; -pub const SOLC_CACHER: OnceCell> = OnceCell::new(); +pub(crate) static SOLC_CACHER: LazyLock>> = LazyLock::new(Default::default); -pub fn get_or_download( +pub(crate) fn get_or_download( working_directory: &Path, downloader: &GHDownloader, ) -> anyhow::Result { - SOLC_CACHER - .get_or_init(|| { - Mutex::new(SolcCacher::new( - working_directory.join(SOLC_CACHE_DIRECTORY), - )) - }) - .lock() - .unwrap() - .get_or_download(downloader) -} + let target_directory = working_directory + .join(SOLC_CACHE_DIRECTORY) + .join(downloader.version.to_string()); + let target_file = target_directory.join(downloader.target); -pub struct SolcCacher { - cache_directory: PathBuf, - cached_binaries: HashSet, -} - -impl SolcCacher { - fn new(cache_directory: PathBuf) -> Self { - Self { - cache_directory, - cached_binaries: Default::default(), - } + let mut cache = SOLC_CACHER.lock().unwrap(); + if cache.contains(&target_file) { + return Ok(target_file); } - fn get_or_download(&mut self, downloader: &GHDownloader) -> anyhow::Result { - let directory = self.cache_directory.join(downloader.version.to_string()); - let file_path = directory.join(downloader.target); + create_dir_all(target_directory)?; + download_to_file(&target_file, downloader)?; + cache.insert(target_file.clone()); - if self.cached_binaries.contains(&file_path) { - return Ok(file_path); - } - - create_dir_all(directory)?; - - let Ok(mut file) = File::create_new(&file_path) else { - self.cached_binaries.insert(file_path.clone()); - return Ok(file_path); - }; - - file.write_all(&downloader.download()?)?; - - #[cfg(unix)] - { - let mut permissions = file.metadata()?.permissions(); - let mode = permissions.mode() | 0o111; - permissions.set_mode(mode); - file.set_permissions(permissions)?; - } - - #[cfg(target_os = "macos")] - std::process::Command::new("xattr") - .arg("-d") - .arg("com.apple.quarantine") - .arg(&file_path) - .stderr(std::process::Stdio::null()) - .stdout(std::process::Stdio::null()) - .stdout(std::process::Stdio::null()) - .spawn()? - .wait()?; - - Ok(file_path) - } + Ok(target_file) +} + +fn download_to_file(path: &Path, downloader: &GHDownloader) -> anyhow::Result<()> { + log::info!("caching file: {}", path.display()); + + let Ok(file) = File::create_new(path) else { + log::warn!("cache file already exists: {}", path.display()); + return Ok(()); + }; + + #[cfg(unix)] + { + let mut permissions = file.metadata()?.permissions(); + permissions.set_mode(permissions.mode() | 0o111); + file.set_permissions(permissions)?; + } + + let mut file = BufWriter::new(file); + file.write_all(&downloader.download()?)?; + file.flush()?; + drop(file); + + #[cfg(target_os = "macos")] + std::process::Command::new("xattr") + .arg("-d") + .arg("com.apple.quarantine") + .arg(&path) + .stderr(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .spawn()? + .wait()?; + + Ok(()) } diff --git a/crates/solc-binaries/src/download.rs b/crates/solc-binaries/src/download.rs index 08859ad..17693f2 100644 --- a/crates/solc-binaries/src/download.rs +++ b/crates/solc-binaries/src/download.rs @@ -1,14 +1,17 @@ //! This module downloads solc binaries. -use std::{collections::HashMap, sync::Mutex}; +use std::{ + collections::HashMap, + sync::{LazyLock, Mutex}, +}; -use once_cell::sync::Lazy; use semver::Version; use sha2::{Digest, Sha256}; use crate::list::List; -pub static LIST_CACHE: Lazy>> = Lazy::new(Default::default); +pub static LIST_CACHE: LazyLock>> = + LazyLock::new(Default::default); impl List { pub const LINUX_URL: &str = "https://binaries.soliditylang.org/linux-amd64/list.json"; @@ -34,6 +37,7 @@ impl List { } /// Download solc binaries from GitHub releases (IPFS links aren't reliable). +#[derive(Clone, Debug)] pub struct GHDownloader { pub version: Version, pub target: &'static str, @@ -82,6 +86,7 @@ impl GHDownloader { /// Errors out if the download fails or the digest of the downloaded file /// mismatches the expected digest from the release [List]. pub fn download(&self) -> anyhow::Result> { + log::info!("downloading solc: {self:?}"); let expected_digest = List::download(self.list)? .builds .iter() diff --git a/crates/solc-binaries/src/lib.rs b/crates/solc-binaries/src/lib.rs index 84ac3b3..aabc86e 100644 --- a/crates/solc-binaries/src/lib.rs +++ b/crates/solc-binaries/src/lib.rs @@ -19,7 +19,7 @@ pub mod list; /// Subsequent calls for the same version will use a cached artifact /// and not download it again. pub fn download_solc( - working_directory: &Path, + cache_directory: &Path, version: Version, wasm: bool, ) -> anyhow::Result { @@ -35,5 +35,5 @@ pub fn download_solc( unimplemented!() }; - get_or_download(working_directory, &downloader) + get_or_download(cache_directory, &downloader) }