From c3771e5d3e293b68418321f8fed48982fe99a33e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Tue, 1 Oct 2024 18:23:36 +0400 Subject: [PATCH] feat: better extra_args handling (#208) ref https://github.com/foundry-rs/foundry/issues/8997 Adds `Solc` constructor for creating solc instances with arguments. Changes `extra_args` to being prepended to actual solc arguments instead of appended as for now. This allows us to use eof binary through `Solc::new_with_args("docker", ["run", ... ])` We didn't end up using those for `--eof-version`, so this shouldn't break anything. Will add a test for this in scope `--eof` flag on foundry side --- crates/compilers/src/compilers/mod.rs | 21 ++++++++----- .../compilers/src/compilers/solc/compiler.rs | 31 +++++++++++++++++-- crates/compilers/src/compilers/solc/mod.rs | 2 +- crates/compilers/src/compilers/vyper/mod.rs | 2 +- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/crates/compilers/src/compilers/mod.rs b/crates/compilers/src/compilers/mod.rs index 8c0ec244..506ea7ca 100644 --- a/crates/compilers/src/compilers/mod.rs +++ b/crates/compilers/src/compilers/mod.rs @@ -278,19 +278,24 @@ pub trait Compiler: Send + Sync + Clone { pub(crate) fn cache_version( path: PathBuf, + args: &[String], f: impl FnOnce(&Path) -> Result, ) -> Result { - static VERSION_CACHE: OnceLock>> = OnceLock::new(); + #[allow(clippy::complexity)] + static VERSION_CACHE: OnceLock, Version>>>> = + OnceLock::new(); let mut lock = VERSION_CACHE .get_or_init(|| Mutex::new(HashMap::new())) .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); - Ok(match lock.entry(path) { - std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), - std::collections::hash_map::Entry::Vacant(entry) => { - let value = f(entry.key())?; - entry.insert(value) - } + + if let Some(version) = lock.get(&path).and_then(|versions| versions.get(args)) { + return Ok(version.clone()); } - .clone()) + + let version = f(&path)?; + + lock.entry(path).or_default().insert(args.to_vec(), version.clone()); + + Ok(version) } diff --git a/crates/compilers/src/compilers/solc/compiler.rs b/crates/compilers/src/compilers/solc/compiler.rs index b95b8e4f..aefcc629 100644 --- a/crates/compilers/src/compilers/solc/compiler.rs +++ b/crates/compilers/src/compilers/solc/compiler.rs @@ -94,6 +94,24 @@ impl Solc { Ok(Self::new_with_version(path, version)) } + /// A new instance which points to `solc` with additional cli arguments. Invokes `solc + /// --version` to determine the version. + /// + /// Returns error if `solc` is not found in the system or if the version cannot be retrieved. + pub fn new_with_args( + path: impl Into, + extra_args: impl IntoIterator>, + ) -> Result { + let args = extra_args.into_iter().map(Into::into).collect::>(); + let path = path.into(); + let version = Self::version_with_args(path.clone(), &args)?; + + let mut solc = Self::new_with_version(path, version); + solc.extra_args = args; + + Ok(solc) + } + /// A new instance which points to `solc` with the given version pub fn new_with_version(path: impl Into, version: Version) -> Self { Self { @@ -429,9 +447,16 @@ impl Solc { /// Invokes `solc --version` and parses the output as a SemVer [`Version`]. #[instrument(level = "debug", skip_all)] pub fn version(solc: impl Into) -> Result { - crate::cache_version(solc.into(), |solc| { + Self::version_with_args(solc, &[]) + } + + /// Invokes `solc --version` and parses the output as a SemVer [`Version`]. + #[instrument(level = "debug", skip_all)] + pub fn version_with_args(solc: impl Into, args: &[String]) -> Result { + crate::cache_version(solc.into(), args, |solc| { let mut cmd = Command::new(solc); - cmd.arg("--version") + cmd.args(args) + .arg("--version") .stdin(Stdio::piped()) .stderr(Stdio::piped()) .stdout(Stdio::piped()); @@ -454,6 +479,7 @@ impl Solc { pub fn configure_cmd(&self) -> Command { let mut cmd = Command::new(&self.solc); cmd.stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped()); + cmd.args(&self.extra_args); if !self.allow_paths.is_empty() { cmd.arg("--allow-paths"); @@ -478,7 +504,6 @@ impl Solc { cmd.current_dir(base_path); } - cmd.args(&self.extra_args); cmd.arg("--standard-json"); cmd diff --git a/crates/compilers/src/compilers/solc/mod.rs b/crates/compilers/src/compilers/solc/mod.rs index 2e33a52c..0bdb6f42 100644 --- a/crates/compilers/src/compilers/solc/mod.rs +++ b/crates/compilers/src/compilers/solc/mod.rs @@ -55,7 +55,7 @@ impl Compiler for SolcCompiler { solc.base_path.clone_from(&input.cli_settings.base_path); solc.allow_paths.clone_from(&input.cli_settings.allow_paths); solc.include_paths.clone_from(&input.cli_settings.include_paths); - solc.extra_args.clone_from(&input.cli_settings.extra_args); + solc.extra_args.extend_from_slice(&input.cli_settings.extra_args); let solc_output = solc.compile(&input.input)?; diff --git a/crates/compilers/src/compilers/vyper/mod.rs b/crates/compilers/src/compilers/vyper/mod.rs index 9912173f..fa64c3ed 100644 --- a/crates/compilers/src/compilers/vyper/mod.rs +++ b/crates/compilers/src/compilers/vyper/mod.rs @@ -166,7 +166,7 @@ impl Vyper { /// Invokes `vyper --version` and parses the output as a SemVer [`Version`]. #[instrument(level = "debug", skip_all)] pub fn version(vyper: impl Into) -> Result { - crate::cache_version(vyper.into(), |vyper| { + crate::cache_version(vyper.into(), &[], |vyper| { let mut cmd = Command::new(vyper); cmd.arg("--version") .stdin(Stdio::piped())