fix(solc): add `RuntimeOrHandle` & fix solc blocking installation (#1260)

* rt wrapper for solc install

* fix

* uncomment feat flag

* feature deps & comment

* async it tests

* use svm::block_install for wasm

* hide rt or handle for wasm

* hide import for wasm
This commit is contained in:
Roman Krasiuk 2022-05-15 02:29:45 +03:00 committed by GitHub
parent eb94e53d1f
commit f3699d08bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 3 deletions

1
Cargo.lock generated
View File

@ -1401,6 +1401,7 @@ dependencies = [
name = "ethers-solc" name = "ethers-solc"
version = "0.3.0" version = "0.3.0"
dependencies = [ dependencies = [
"cfg-if 1.0.0",
"colored", "colored",
"criterion", "criterion",
"dunce", "dunce",

View File

@ -19,7 +19,7 @@ serde_json = "1.0.68"
serde = { version = "1.0.130", features = ["derive"] } serde = { version = "1.0.130", features = ["derive"] }
semver = { version = "1.0.9", features = ["serde"] } semver = { version = "1.0.9", features = ["serde"] }
walkdir = "2.3.2" walkdir = "2.3.2"
tokio = { version = "1.15.0", default-features = false, features = ["process", "io-util", "fs", "time"], optional = true } tokio = { version = "1.15.0", default-features = false, features = ["rt"] }
futures-util = { version = "^0.3", optional = true } futures-util = { version = "^0.3", optional = true }
once_cell = "1.10.0" once_cell = "1.10.0"
regex = "1.5.5" regex = "1.5.5"
@ -39,6 +39,7 @@ solang-parser = { default-features = false, version = "=0.1.13" }
rayon = "1.5.2" rayon = "1.5.2"
rand = { version = "0.8.5", optional = true } rand = { version = "0.8.5", optional = true }
path-slash = "0.1.4" path-slash = "0.1.4"
cfg-if = "1.0.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
home = "0.5.3" home = "0.5.3"
@ -80,7 +81,7 @@ required-features = ["full", "project-util"]
[features] [features]
default = ["rustls"] default = ["rustls"]
async = ["tokio", "futures-util"] async = ["tokio/process", "tokio/io-util", "tokio/fs", "tokio/time", "futures-util"]
full = ["async", "svm-solc"] full = ["async", "svm-solc"]
svm-solc = ["svm/blocking", "svm-builds", "sha2"] svm-solc = ["svm/blocking", "svm-builds", "sha2"]
# Utilities for creating and testing project workspaces # Utilities for creating and testing project workspaces

View File

@ -371,9 +371,21 @@ impl Solc {
/// Blocking version of `Self::install` /// Blocking version of `Self::install`
#[cfg(all(feature = "svm-solc"))] #[cfg(all(feature = "svm-solc"))]
pub fn blocking_install(version: &Version) -> std::result::Result<Self, svm::SolcVmError> { pub fn blocking_install(version: &Version) -> std::result::Result<Self, svm::SolcVmError> {
use crate::utils::RuntimeOrHandle;
tracing::trace!("blocking installing solc version \"{}\"", version); tracing::trace!("blocking installing solc version \"{}\"", version);
crate::report::solc_installation_start(version); crate::report::solc_installation_start(version);
match svm::blocking_install(version) { // the async version `svm::install` is used instead of `svm::blocking_intsall`
// because the underlying `reqwest::blocking::Client` does not behave well
// in tokio rt. see https://github.com/seanmonstar/reqwest/issues/1017
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
let installation = svm::blocking_install(version);
} else {
let installation = RuntimeOrHandle::new().block_on(svm::install(version));
}
};
match installation {
Ok(path) => { Ok(path) => {
crate::report::solc_installation_success(version); crate::report::solc_installation_success(version);
Ok(Solc::new(path)) Ok(Solc::new(path))
@ -723,6 +735,7 @@ mod tests {
let other = solc().async_compile(&serde_json::json!(input)).await.unwrap(); let other = solc().async_compile(&serde_json::json!(input)).await.unwrap();
assert_eq!(out, other); assert_eq!(out, other);
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]
#[tokio::test] #[tokio::test]
async fn async_solc_compile_works2() { async fn async_solc_compile_works2() {
@ -799,6 +812,15 @@ mod tests {
assert_eq!(res.solc, expected); assert_eq!(res.solc, expected);
} }
#[test]
#[cfg(feature = "svm-solc")]
fn can_install_solc_in_tokio_rt() {
let version = Version::from_str("0.8.6").unwrap();
let rt = tokio::runtime::Runtime::new().unwrap();
let result = rt.block_on(async { Solc::blocking_install(&version) });
assert!(result.is_ok());
}
#[test] #[test]
fn does_not_find_not_installed_version() { fn does_not_find_not_installed_version() {
let ver = "1.1.1"; let ver = "1.1.1";

View File

@ -330,6 +330,40 @@ pub(crate) fn find_fave_or_alt_path(root: impl AsRef<Path>, fave: &str, alt: &st
p p
} }
#[cfg(not(target_arch = "wasm32"))]
use tokio::runtime::{Handle, Runtime};
#[cfg(not(target_arch = "wasm32"))]
#[derive(Debug)]
pub enum RuntimeOrHandle {
Runtime(Runtime),
Handle(Handle),
}
#[cfg(not(target_arch = "wasm32"))]
impl Default for RuntimeOrHandle {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(target_arch = "wasm32"))]
impl RuntimeOrHandle {
pub fn new() -> RuntimeOrHandle {
match Handle::try_current() {
Ok(handle) => RuntimeOrHandle::Handle(handle),
Err(_) => RuntimeOrHandle::Runtime(Runtime::new().expect("Failed to start runtime")),
}
}
pub fn block_on<F: std::future::Future>(&self, f: F) -> F::Output {
match &self {
RuntimeOrHandle::Runtime(runtime) => runtime.block_on(f),
RuntimeOrHandle::Handle(handle) => tokio::task::block_in_place(|| handle.block_on(f)),
}
}
}
/// Creates a new named tempdir /// Creates a new named tempdir
#[cfg(any(test, feature = "project-util"))] #[cfg(any(test, feature = "project-util"))]
pub(crate) fn tempdir(name: &str) -> Result<tempfile::TempDir, SolcIoError> { pub(crate) fn tempdir(name: &str) -> Result<tempfile::TempDir, SolcIoError> {

View File

@ -17,6 +17,7 @@ use ethers_solc::{
ProjectPathsConfig, Solc, TestFileFilter, ProjectPathsConfig, Solc, TestFileFilter,
}; };
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use semver::Version;
#[allow(unused)] #[allow(unused)]
fn init_tracing() { fn init_tracing() {
@ -1329,3 +1330,53 @@ fn can_compile_model_checker_sample() {
assert!(!compiled.has_compiler_errors()); assert!(!compiled.has_compiler_errors());
assert!(compiled.has_compiler_warnings()); assert!(compiled.has_compiler_warnings());
} }
fn remove_solc_if_exists(version: &Version) {
match Solc::find_svm_installed_version(version.to_string()).unwrap() {
Some(_) => svm::remove_version(&version).expect("failed to remove version"),
None => {}
};
}
#[tokio::test(flavor = "multi_thread")]
async fn can_install_solc_and_compile_version() {
let project = TempProject::dapptools().unwrap();
let version = Version::new(0, 8, 10);
project
.add_source(
"Contract",
format!(
r#"
pragma solidity {};
contract Contract {{ }}
"#,
version.to_string()
),
)
.unwrap();
remove_solc_if_exists(&version);
let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
}
#[tokio::test(flavor = "multi_thread")]
async fn can_install_solc_and_compile_std_json_input_async() {
let tmp = TempProject::dapptools_init().unwrap();
tmp.assert_no_errors();
let source = tmp.list_source_files().into_iter().find(|p| p.ends_with("Dapp.t.sol")).unwrap();
let input = tmp.project().standard_json_input(source).unwrap();
let solc = &tmp.project().solc;
assert!(input.settings.remappings.contains(&"ds-test/=lib/ds-test/src/".parse().unwrap()));
let input: CompilerInput = input.into();
assert!(input.sources.contains_key(Path::new("lib/ds-test/src/test.sol")));
remove_solc_if_exists(&solc.version().expect("failed to get version"));
let out = solc.async_compile(&input).await.unwrap();
assert!(!out.has_error());
assert!(out.sources.contains_key("lib/ds-test/src/test.sol"));
}