check the supported solc version

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
Cyrill Leutwiler
2025-03-25 11:20:00 +01:00
parent 382b944bd1
commit a835754d41
7 changed files with 87 additions and 73 deletions
Generated
+1 -1
View File
@@ -2937,7 +2937,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hex", "hex",
"once_cell", "log",
"reqwest", "reqwest",
"semver 1.0.26", "semver 1.0.26",
"serde", "serde",
+5 -3
View File
@@ -11,7 +11,6 @@ use revive_dt_format::{
use revive_dt_node::Node; use revive_dt_node::Node;
use revive_dt_solc_binaries::download_solc; use revive_dt_solc_binaries::download_solc;
use revive_solc_json_interface::SolcStandardJsonOutput; use revive_solc_json_interface::SolcStandardJsonOutput;
use semver::Version;
use crate::Platform; use crate::Platform;
@@ -41,19 +40,22 @@ where
} }
pub fn build_contracts(&mut self, mode: &SolcMode, metadata: &Metadata) -> anyhow::Result<()> { 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 sources = metadata.contract_sources()?;
let base_path = metadata.directory()?.display().to_string(); let base_path = metadata.directory()?.display().to_string();
let mut compiler = Compiler::<T::Compiler>::new().base_path(base_path.clone()); let mut compiler = Compiler::<T::Compiler>::new().base_path(base_path.clone());
for (file, _contract) in sources.values() { for (file, _contract) in sources.values() {
compiler = compiler.with_source(file)?; 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 solc_path = download_solc(self.config.directory(), version, self.config.wasm)?;
let output = compiler let output = compiler
.solc_optimizer(mode.solc_optimize()) .solc_optimizer(mode.solc_optimize())
.try_build(solc_path)?; .try_build(solc_path)?;
self.contracts.insert(output.input, output.output); self.contracts.insert(output.input, output.output);
Ok(()) Ok(())
+21
View File
@@ -1,3 +1,4 @@
use semver::Version;
use serde::Deserialize; use serde::Deserialize;
use serde::de::Deserializer; use serde::de::Deserializer;
@@ -57,6 +58,26 @@ impl SolcMode {
pub fn solc_optimize(&self) -> bool { pub fn solc_optimize(&self) -> bool {
self.solc_optimize.unwrap_or(true) 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<Version> {
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 { impl<'de> Deserialize<'de> for Mode {
+1 -1
View File
@@ -11,7 +11,7 @@ rust-version.workspace = true
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
once_cell = { workspace = true } log = { workspace = true }
reqwest = { workspace = true } reqwest = { workspace = true }
semver = { workspace = true } semver = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
+49 -63
View File
@@ -1,84 +1,70 @@
//! Helper for caching the solc binaries. //! Helper for caching the solc binaries.
use std::{ use std::{
cell::OnceCell,
collections::HashSet, collections::HashSet,
fs::{File, create_dir_all}, fs::{File, create_dir_all},
io::Write, io::{BufWriter, Write},
os::unix::fs::PermissionsExt, os::unix::fs::PermissionsExt,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Mutex, sync::{LazyLock, Mutex},
}; };
use crate::download::GHDownloader; use crate::download::GHDownloader;
pub const SOLC_CACHE_DIRECTORY: &str = "solc"; pub const SOLC_CACHE_DIRECTORY: &str = "solc";
pub const SOLC_CACHER: OnceCell<Mutex<SolcCacher>> = OnceCell::new(); pub(crate) static SOLC_CACHER: LazyLock<Mutex<HashSet<PathBuf>>> = LazyLock::new(Default::default);
pub fn get_or_download( pub(crate) fn get_or_download(
working_directory: &Path, working_directory: &Path,
downloader: &GHDownloader, downloader: &GHDownloader,
) -> anyhow::Result<PathBuf> { ) -> anyhow::Result<PathBuf> {
SOLC_CACHER let target_directory = working_directory
.get_or_init(|| { .join(SOLC_CACHE_DIRECTORY)
Mutex::new(SolcCacher::new( .join(downloader.version.to_string());
working_directory.join(SOLC_CACHE_DIRECTORY), let target_file = target_directory.join(downloader.target);
))
})
.lock()
.unwrap()
.get_or_download(downloader)
}
pub struct SolcCacher { let mut cache = SOLC_CACHER.lock().unwrap();
cache_directory: PathBuf, if cache.contains(&target_file) {
cached_binaries: HashSet<PathBuf>, return Ok(target_file);
}
impl SolcCacher {
fn new(cache_directory: PathBuf) -> Self {
Self {
cache_directory,
cached_binaries: Default::default(),
}
} }
fn get_or_download(&mut self, downloader: &GHDownloader) -> anyhow::Result<PathBuf> { create_dir_all(target_directory)?;
let directory = self.cache_directory.join(downloader.version.to_string()); download_to_file(&target_file, downloader)?;
let file_path = directory.join(downloader.target); cache.insert(target_file.clone());
if self.cached_binaries.contains(&file_path) { Ok(target_file)
return Ok(file_path); }
}
fn download_to_file(path: &Path, downloader: &GHDownloader) -> anyhow::Result<()> {
create_dir_all(directory)?; log::info!("caching file: {}", path.display());
let Ok(mut file) = File::create_new(&file_path) else { let Ok(file) = File::create_new(path) else {
self.cached_binaries.insert(file_path.clone()); log::warn!("cache file already exists: {}", path.display());
return Ok(file_path); return Ok(());
}; };
file.write_all(&downloader.download()?)?; #[cfg(unix)]
{
#[cfg(unix)] let mut permissions = file.metadata()?.permissions();
{ permissions.set_mode(permissions.mode() | 0o111);
let mut permissions = file.metadata()?.permissions(); file.set_permissions(permissions)?;
let mode = permissions.mode() | 0o111; }
permissions.set_mode(mode);
file.set_permissions(permissions)?; let mut file = BufWriter::new(file);
} file.write_all(&downloader.download()?)?;
file.flush()?;
#[cfg(target_os = "macos")] drop(file);
std::process::Command::new("xattr")
.arg("-d") #[cfg(target_os = "macos")]
.arg("com.apple.quarantine") std::process::Command::new("xattr")
.arg(&file_path) .arg("-d")
.stderr(std::process::Stdio::null()) .arg("com.apple.quarantine")
.stdout(std::process::Stdio::null()) .arg(&path)
.stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null())
.spawn()? .stdout(std::process::Stdio::null())
.wait()?; .stdout(std::process::Stdio::null())
.spawn()?
Ok(file_path) .wait()?;
}
Ok(())
} }
+8 -3
View File
@@ -1,14 +1,17 @@
//! This module downloads solc binaries. //! 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 semver::Version;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use crate::list::List; use crate::list::List;
pub static LIST_CACHE: Lazy<Mutex<HashMap<&'static str, List>>> = Lazy::new(Default::default); pub static LIST_CACHE: LazyLock<Mutex<HashMap<&'static str, List>>> =
LazyLock::new(Default::default);
impl List { impl List {
pub const LINUX_URL: &str = "https://binaries.soliditylang.org/linux-amd64/list.json"; 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). /// Download solc binaries from GitHub releases (IPFS links aren't reliable).
#[derive(Clone, Debug)]
pub struct GHDownloader { pub struct GHDownloader {
pub version: Version, pub version: Version,
pub target: &'static str, pub target: &'static str,
@@ -82,6 +86,7 @@ impl GHDownloader {
/// Errors out if the download fails or the digest of the downloaded file /// Errors out if the download fails or the digest of the downloaded file
/// mismatches the expected digest from the release [List]. /// mismatches the expected digest from the release [List].
pub fn download(&self) -> anyhow::Result<Vec<u8>> { pub fn download(&self) -> anyhow::Result<Vec<u8>> {
log::info!("downloading solc: {self:?}");
let expected_digest = List::download(self.list)? let expected_digest = List::download(self.list)?
.builds .builds
.iter() .iter()
+2 -2
View File
@@ -19,7 +19,7 @@ pub mod list;
/// Subsequent calls for the same version will use a cached artifact /// Subsequent calls for the same version will use a cached artifact
/// and not download it again. /// and not download it again.
pub fn download_solc( pub fn download_solc(
working_directory: &Path, cache_directory: &Path,
version: Version, version: Version,
wasm: bool, wasm: bool,
) -> anyhow::Result<PathBuf> { ) -> anyhow::Result<PathBuf> {
@@ -35,5 +35,5 @@ pub fn download_solc(
unimplemented!() unimplemented!()
}; };
get_or_download(working_directory, &downloader) get_or_download(cache_directory, &downloader)
} }