Use the solc version required in tests rather than one on PATH

This commit is contained in:
James Wilson
2025-08-21 12:23:54 +01:00
parent 85033cfead
commit 491a9a32b2
11 changed files with 263 additions and 307 deletions
Generated
+1
View File
@@ -4526,6 +4526,7 @@ dependencies = [
"revive-dt-node",
"revive-dt-node-interaction",
"revive-dt-report",
"revive-dt-solc-binaries",
"semver 1.0.26",
"serde",
"serde_json",
@@ -6,6 +6,42 @@ pub enum VersionOrRequirement {
Requirement(VersionReq),
}
impl VersionOrRequirement {
/// A helper function to convert a [`semver::Version`] into a [`semver::VersionReq`].
pub fn version_to_requirement(version: &Version) -> VersionReq {
// Ignoring "build" metadata in the version, we can turn
// it into a requirement which is an exact match for the
// given version and nothing else:
VersionReq {
comparators: vec![semver::Comparator {
op: semver::Op::Exact,
major: version.major,
minor: Some(version.minor),
patch: Some(version.patch),
pre: version.pre.clone(),
}],
}
}
}
impl serde::Serialize for VersionOrRequirement {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
VersionOrRequirement::Version(v) => serializer.serialize_str(&v.to_string()),
VersionOrRequirement::Requirement(r) => serializer.serialize_str(&r.to_string()),
}
}
}
impl Default for VersionOrRequirement {
fn default() -> Self {
VersionOrRequirement::Requirement(VersionReq::STAR)
}
}
impl From<Version> for VersionOrRequirement {
fn from(value: Version) -> Self {
Self::Version(value)
@@ -18,24 +54,45 @@ impl From<VersionReq> for VersionOrRequirement {
}
}
impl From<VersionOrRequirement> for VersionReq {
fn from(value: VersionOrRequirement) -> Self {
match value {
VersionOrRequirement::Version(version) => {
VersionOrRequirement::version_to_requirement(&version)
}
VersionOrRequirement::Requirement(version_req) => version_req,
}
}
}
impl TryFrom<VersionOrRequirement> for Version {
type Error = anyhow::Error;
fn try_from(value: VersionOrRequirement) -> Result<Self, Self::Error> {
let VersionOrRequirement::Version(version) = value else {
anyhow::bail!("Version or requirement was not a version");
};
Ok(version)
}
}
impl TryFrom<VersionOrRequirement> for VersionReq {
type Error = anyhow::Error;
fn try_from(value: VersionOrRequirement) -> Result<Self, Self::Error> {
let VersionOrRequirement::Requirement(requirement) = value else {
anyhow::bail!("Version or requirement was not a requirement");
};
Ok(requirement)
match value {
VersionOrRequirement::Version(version) => Ok(version),
VersionOrRequirement::Requirement(mut version_req) => {
if version_req.comparators.len() != 1 {
anyhow::bail!(
"The version requirement in VersionOrRequirement is not a single exact version"
);
}
let c = version_req.comparators.pop().unwrap();
let (semver::Op::Exact, Some(minor), Some(patch)) = (c.op, c.minor, c.patch) else {
anyhow::bail!(
"The version requirement in VersionOrRequirement is not an exact version"
);
};
Ok(Version {
major: c.major,
minor,
patch,
pre: c.pre,
build: Default::default(),
})
}
}
}
}
+31 -27
View File
@@ -4,6 +4,7 @@
//! - Polkadot revive Wasm compiler
mod constants;
mod utils;
use std::{
collections::HashMap,
@@ -13,12 +14,11 @@ use std::{
use alloy::json_abi::JsonAbi;
use alloy_primitives::Address;
use semver::Version;
use semver::VersionReq;
use serde::{Deserialize, Serialize};
use revive_common::EVMVersion;
use revive_dt_common::cached_fs::read_to_string;
use revive_dt_common::types::VersionOrRequirement;
use revive_dt_config::Arguments;
// Re-export this as it's a part of the compiler interface.
@@ -40,28 +40,20 @@ pub trait SolidityCompiler {
additional_options: Self::Options,
) -> impl Future<Output = anyhow::Result<CompilerOutput>>;
fn new(solc_executable: PathBuf) -> Self;
fn get_compiler_executable(
config: &Arguments,
version: impl Into<VersionOrRequirement>,
) -> impl Future<Output = anyhow::Result<PathBuf>>;
fn version(&self) -> impl Future<Output = anyhow::Result<Version>>;
/// Instantiate a new compiler.
fn new(config: &Arguments) -> Self;
/// Does the compiler support the provided mode and version settings?
fn supports_mode(
compiler_version: &Version,
optimize_setting: ModeOptimizerSetting,
pipeline: ModePipeline,
) -> bool;
fn supports_mode(optimize_setting: ModeOptimizerSetting, pipeline: ModePipeline) -> bool;
}
/// The generic compilation input configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize)]
pub struct CompilerInput {
pub wasm: bool,
pub pipeline: Option<ModePipeline>,
pub optimization: Option<ModeOptimizerSetting>,
pub solc_version: Option<VersionReq>,
pub evm_version: Option<EVMVersion>,
pub allow_paths: Vec<PathBuf>,
pub base_path: Option<PathBuf>,
@@ -84,12 +76,6 @@ pub struct Compiler<T: SolidityCompiler> {
additional_options: T::Options,
}
impl Default for Compiler<solc::Solc> {
fn default() -> Self {
Self::new()
}
}
impl<T> Compiler<T>
where
T: SolidityCompiler,
@@ -97,8 +83,10 @@ where
pub fn new() -> Self {
Self {
input: CompilerInput {
wasm: Default::default(),
pipeline: Default::default(),
optimization: Default::default(),
solc_version: Default::default(),
evm_version: Default::default(),
allow_paths: Default::default(),
base_path: Default::default(),
@@ -110,6 +98,16 @@ where
}
}
pub fn with_wasm(mut self, value: bool) -> Self {
self.input.wasm = value;
self
}
pub fn with_solc_version_req(mut self, value: impl Into<Option<VersionReq>>) -> Self {
self.input.solc_version = value.into();
self
}
pub fn with_optimization(mut self, value: impl Into<Option<ModeOptimizerSetting>>) -> Self {
self.input.optimization = value.into();
self
@@ -177,11 +175,8 @@ where
callback(self)
}
pub async fn try_build(
self,
compiler_path: impl AsRef<Path>,
) -> anyhow::Result<CompilerOutput> {
T::new(compiler_path.as_ref().to_path_buf())
pub async fn try_build(self, config: &Arguments) -> anyhow::Result<CompilerOutput> {
T::new(config)
.build(self.input, self.additional_options)
.await
}
@@ -191,6 +186,15 @@ where
}
}
impl<T> Default for Compiler<T>
where
T: SolidityCompiler,
{
fn default() -> Self {
Self::new()
}
}
/// Defines how the compiler should handle revert strings.
#[derive(
Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize,
+32 -83
View File
@@ -1,13 +1,8 @@
//! Implements the [SolidityCompiler] trait with `resolc` for
//! compiling contracts to PolkaVM (PVM) bytecode.
use std::{
path::PathBuf,
process::{Command, Stdio},
sync::LazyLock,
};
use std::{path::PathBuf, process::Stdio};
use dashmap::DashMap;
use revive_dt_common::types::VersionOrRequirement;
use revive_dt_config::Arguments;
use revive_solc_json_interface::{
@@ -17,6 +12,7 @@ use revive_solc_json_interface::{
};
use super::constants::SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
use super::utils;
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler};
use alloy::json_abi::JsonAbi;
@@ -31,6 +27,11 @@ use tokio::{io::AsyncWriteExt, process::Command as AsyncCommand};
/// A wrapper around the `resolc` binary, emitting PVM-compatible bytecode.
#[derive(Debug)]
pub struct Resolc {
// Where to cache artifacts.
cache_directory: PathBuf,
// We'll use this version when no explicit version
// requirement is given in the test mode.
solc_version: Version,
/// Path to the `resolc` executable
resolc_path: PathBuf,
}
@@ -42,8 +43,10 @@ impl SolidityCompiler for Resolc {
async fn build(
&self,
CompilerInput {
wasm,
pipeline,
optimization,
solc_version,
evm_version,
allow_paths,
base_path,
@@ -61,6 +64,19 @@ impl SolidityCompiler for Resolc {
);
}
let solc_version_req = solc_version
.unwrap_or_else(|| VersionOrRequirement::version_to_requirement(&self.solc_version));
let solc_path =
revive_dt_solc_binaries::download_solc(&self.cache_directory, solc_version_req, wasm)
.await?;
let solc_version = utils::solc_version(&solc_path).await?;
if solc_version < SOLC_VERSION_SUPPORTING_VIA_YUL_IR {
anyhow::bail!(
"We are trying to run the test with solc version {solc_version}, but require {SOLC_VERSION_SUPPORTING_VIA_YUL_IR} or greater"
);
}
let input = SolcStandardJsonInput {
language: SolcStandardJsonInputLanguage::Solidity,
sources: sources
@@ -106,6 +122,8 @@ impl SolidityCompiler for Resolc {
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.arg("--solc")
.arg(&solc_path)
.arg("--standard-json");
if let Some(ref base_path) = base_path {
@@ -206,86 +224,17 @@ impl SolidityCompiler for Resolc {
Ok(compiler_output)
}
fn new(resolc_path: PathBuf) -> Self {
Resolc { resolc_path }
}
async fn get_compiler_executable(
config: &Arguments,
_version: impl Into<VersionOrRequirement>,
) -> anyhow::Result<PathBuf> {
if !config.resolc.as_os_str().is_empty() {
return Ok(config.resolc.clone());
}
Ok(PathBuf::from("resolc"))
}
async fn version(&self) -> anyhow::Result<semver::Version> {
/// This is a cache of the path of the compiler to the version number of the compiler. We
/// choose to cache the version in this way rather than through a field on the struct since
/// compiler objects are being created all the time from the path and the compiler object is
/// not reused over time.
static VERSION_CACHE: LazyLock<DashMap<PathBuf, Version>> = LazyLock::new(Default::default);
match VERSION_CACHE.entry(self.resolc_path.clone()) {
dashmap::Entry::Occupied(occupied_entry) => Ok(occupied_entry.get().clone()),
dashmap::Entry::Vacant(vacant_entry) => {
let output = Command::new(self.resolc_path.as_path())
.arg("--version")
.stdout(Stdio::piped())
.spawn()?
.wait_with_output()?
.stdout;
let output = String::from_utf8_lossy(&output);
let version_string = output
.split("version ")
.nth(1)
.context("Version parsing failed")?
.split("+")
.next()
.context("Version parsing failed")?;
let version = Version::parse(version_string)?;
vacant_entry.insert(version.clone());
Ok(version)
}
fn new(config: &Arguments) -> Self {
Resolc {
cache_directory: config.directory().to_path_buf(),
solc_version: config.solc.clone(),
resolc_path: config.resolc.clone(),
}
}
fn supports_mode(
compiler_version: &Version,
_optimize_setting: ModeOptimizerSetting,
pipeline: ModePipeline,
) -> bool {
// We only support the Y (IE compile via Yul IR) mode here, which also means that we can
// only use solc version 0.8.13 and above. We must always compile via Yul IR as resolc
// needs this to translate to LLVM IR and then RISCV.
fn supports_mode(_optimize_setting: ModeOptimizerSetting, pipeline: ModePipeline) -> bool {
// We only support the Y (IE compile via Yul IR) mode here. We must always compile
// via Yul IR as resolc needs this to translate to LLVM IR and then RISCV.
pipeline == ModePipeline::ViaYulIR
&& compiler_version >= &SOLC_VERSION_SUPPORTING_VIA_YUL_IR
}
}
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn compiler_version_can_be_obtained() {
// Arrange
let args = Arguments::default();
let path = Resolc::get_compiler_executable(&args, Version::new(0, 7, 6))
.await
.unwrap();
let compiler = Resolc::new(path);
// Act
let version = compiler.version().await;
// Assert
let _ = version.expect("Failed to get version");
}
}
+23 -110
View File
@@ -1,18 +1,13 @@
//! Implements the [SolidityCompiler] trait with solc for
//! compiling contracts to EVM bytecode.
use std::{
path::PathBuf,
process::{Command, Stdio},
sync::LazyLock,
};
use std::{path::PathBuf, process::Stdio};
use dashmap::DashMap;
use revive_dt_common::types::VersionOrRequirement;
use revive_dt_config::Arguments;
use revive_dt_solc_binaries::download_solc;
use super::constants::SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
use super::utils;
use crate::{CompilerInput, CompilerOutput, ModeOptimizerSetting, ModePipeline, SolidityCompiler};
use anyhow::Context;
@@ -28,7 +23,11 @@ use tokio::{io::AsyncWriteExt, process::Command as AsyncCommand};
#[derive(Debug)]
pub struct Solc {
solc_path: PathBuf,
// Where to cache artifacts.
cache_directory: PathBuf,
// We'll use this version when no explicit version requirement
// is given in the test mode.
solc_version: Version,
}
impl SolidityCompiler for Solc {
@@ -38,8 +37,10 @@ impl SolidityCompiler for Solc {
async fn build(
&self,
CompilerInput {
wasm,
pipeline,
optimization,
solc_version,
evm_version,
allow_paths,
base_path,
@@ -49,7 +50,13 @@ impl SolidityCompiler for Solc {
}: CompilerInput,
_: Self::Options,
) -> anyhow::Result<CompilerOutput> {
let compiler_supports_via_ir = self.version().await? >= SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
let solc_version = solc_version
.unwrap_or_else(|| VersionOrRequirement::version_to_requirement(&self.solc_version));
let solc_path =
revive_dt_solc_binaries::download_solc(&self.cache_directory, solc_version, wasm)
.await?;
let compiler_supports_via_ir =
utils::solc_version(&solc_path).await? >= SOLC_VERSION_SUPPORTING_VIA_YUL_IR;
// Be careful to entirely omit the viaIR field if the compiler does not support it,
// as it will error if you provide fields it does not know about. Because
@@ -115,7 +122,7 @@ impl SolidityCompiler for Solc {
},
};
let mut command = AsyncCommand::new(&self.solc_path);
let mut command = AsyncCommand::new(&solc_path);
command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
@@ -199,110 +206,16 @@ impl SolidityCompiler for Solc {
Ok(compiler_output)
}
fn new(solc_path: PathBuf) -> Self {
Self { solc_path }
}
async fn get_compiler_executable(
config: &Arguments,
version: impl Into<VersionOrRequirement>,
) -> anyhow::Result<PathBuf> {
let path = download_solc(config.directory(), version, config.wasm).await?;
Ok(path)
}
async fn version(&self) -> anyhow::Result<semver::Version> {
/// This is a cache of the path of the compiler to the version number of the compiler. We
/// choose to cache the version in this way rather than through a field on the struct since
/// compiler objects are being created all the time from the path and the compiler object is
/// not reused over time.
static VERSION_CACHE: LazyLock<DashMap<PathBuf, Version>> = LazyLock::new(Default::default);
match VERSION_CACHE.entry(self.solc_path.clone()) {
dashmap::Entry::Occupied(occupied_entry) => Ok(occupied_entry.get().clone()),
dashmap::Entry::Vacant(vacant_entry) => {
// The following is the parsing code for the version from the solc version strings
// which look like the following:
// ```
// solc, the solidity compiler commandline interface
// Version: 0.8.30+commit.73712a01.Darwin.appleclang
// ```
let child = Command::new(self.solc_path.as_path())
.arg("--version")
.stdout(Stdio::piped())
.spawn()?;
let output = child.wait_with_output()?;
let output = String::from_utf8_lossy(&output.stdout);
let version_line = output
.split("Version: ")
.nth(1)
.context("Version parsing failed")?;
let version_string = version_line
.split("+")
.next()
.context("Version parsing failed")?;
let version = Version::parse(version_string)?;
vacant_entry.insert(version.clone());
Ok(version)
}
fn new(config: &Arguments) -> Self {
Self {
cache_directory: config.directory().to_path_buf(),
solc_version: config.solc.clone(),
}
}
fn supports_mode(
compiler_version: &Version,
_optimize_setting: ModeOptimizerSetting,
pipeline: ModePipeline,
) -> bool {
fn supports_mode(_optimize_setting: ModeOptimizerSetting, pipeline: ModePipeline) -> bool {
// solc 0.8.13 and above supports --via-ir, and less than that does not. Thus, we support mode E
// (ie no Yul IR) in either case, but only support Y (via Yul IR) if the compiler is new enough.
pipeline == ModePipeline::ViaEVMAssembly
|| (pipeline == ModePipeline::ViaYulIR
&& compiler_version >= &SOLC_VERSION_SUPPORTING_VIA_YUL_IR)
}
}
#[cfg(test)]
mod test {
use super::*;
#[tokio::test]
async fn compiler_version_can_be_obtained() {
// Arrange
let args = Arguments::default();
let path = Solc::get_compiler_executable(&args, Version::new(0, 7, 6))
.await
.unwrap();
let compiler = Solc::new(path);
// Act
let version = compiler.version().await;
// Assert
assert_eq!(
version.expect("Failed to get version"),
Version::new(0, 7, 6)
)
}
#[tokio::test]
async fn compiler_version_can_be_obtained1() {
// Arrange
let args = Arguments::default();
let path = Solc::get_compiler_executable(&args, Version::new(0, 4, 21))
.await
.unwrap();
let compiler = Solc::new(path);
// Act
let version = compiler.version().await;
// Assert
assert_eq!(
version.expect("Failed to get version"),
Version::new(0, 4, 21)
)
pipeline == ModePipeline::ViaEVMAssembly || pipeline == ModePipeline::ViaYulIR
}
}
+50
View File
@@ -0,0 +1,50 @@
use std::{
path::{Path, PathBuf},
process::{Command, Stdio},
sync::LazyLock,
};
use anyhow::Context;
use dashmap::DashMap;
use semver::Version;
/// Fetch the solc version given a path to the executable
pub async fn solc_version(solc_path: &Path) -> anyhow::Result<semver::Version> {
/// This is a cache of the path of the compiler to the version number of the compiler. We
/// choose to cache the version in this way rather than through a field on the struct since
/// compiler objects are being created all the time from the path and the compiler object is
/// not reused over time.
static VERSION_CACHE: LazyLock<DashMap<PathBuf, Version>> = LazyLock::new(Default::default);
match VERSION_CACHE.entry(solc_path.to_path_buf()) {
dashmap::Entry::Occupied(occupied_entry) => Ok(occupied_entry.get().clone()),
dashmap::Entry::Vacant(vacant_entry) => {
// The following is the parsing code for the version from the solc version strings
// which look like the following:
// ```
// solc, the solidity compiler commandline interface
// Version: 0.8.30+commit.73712a01.Darwin.appleclang
// ```
let child = Command::new(solc_path)
.arg("--version")
.stdout(Stdio::piped())
.spawn()?;
let output = child.wait_with_output()?;
let output = String::from_utf8_lossy(&output.stdout);
let version_line = output
.split("Version: ")
.nth(1)
.context("Version parsing failed")?;
let version_string = version_line
.split("+")
.next()
.context("Version parsing failed")?;
let version = Version::parse(version_string)?;
vacant_entry.insert(version.clone());
Ok(version)
}
}
}
+3 -10
View File
@@ -1,16 +1,12 @@
use std::path::PathBuf;
use revive_dt_compiler::{Compiler, SolidityCompiler, revive_resolc::Resolc, solc::Solc};
use revive_dt_compiler::{Compiler, revive_resolc::Resolc, solc::Solc};
use revive_dt_config::Arguments;
use semver::Version;
#[tokio::test]
async fn contracts_can_be_compiled_with_solc() {
// Arrange
let args = Arguments::default();
let compiler_path = Solc::get_compiler_executable(&args, Version::new(0, 8, 30))
.await
.unwrap();
// Act
let output = Compiler::<Solc>::new()
@@ -18,7 +14,7 @@ async fn contracts_can_be_compiled_with_solc() {
.unwrap()
.with_source("./tests/assets/array_one_element/main.sol")
.unwrap()
.try_build(compiler_path)
.try_build(&args)
.await;
// Assert
@@ -49,9 +45,6 @@ async fn contracts_can_be_compiled_with_solc() {
async fn contracts_can_be_compiled_with_resolc() {
// Arrange
let args = Arguments::default();
let compiler_path = Resolc::get_compiler_executable(&args, Version::new(0, 8, 30))
.await
.unwrap();
// Act
let output = Compiler::<Resolc>::new()
@@ -59,7 +52,7 @@ async fn contracts_can_be_compiled_with_resolc() {
.unwrap()
.with_source("./tests/assets/array_one_element/main.sol")
.unwrap()
.try_build(compiler_path)
.try_build(&args)
.await;
// Assert
+1
View File
@@ -20,6 +20,7 @@ revive-dt-format = { workspace = true }
revive-dt-node = { workspace = true }
revive-dt-node-interaction = { workspace = true }
revive-dt-report = { workspace = true }
revive-dt-solc-binaries = { workspace = true }
alloy = { workspace = true }
anyhow = { workspace = true }
+7 -12
View File
@@ -9,9 +9,10 @@ use std::{
use futures::FutureExt;
use revive_dt_common::iterators::FilesWithExtensionIterator;
use revive_dt_compiler::{Compiler, CompilerOutput, Mode, SolidityCompiler};
use revive_dt_compiler::{Compiler, CompilerOutput, Mode};
use revive_dt_config::Arguments;
use revive_dt_format::metadata::{ContractIdent, ContractInstance, Metadata};
use revive_dt_solc_binaries::solc_version;
use alloy::{hex::ToHexExt, json_abi::JsonAbi, primitives::Address};
use anyhow::{Error, Result};
@@ -57,14 +58,7 @@ impl CachedCompiler {
Lazy::new(Default::default);
let compiler_version_or_requirement = mode.compiler_version_to_use(config.solc.clone());
let compiler_path = <P::Compiler as SolidityCompiler>::get_compiler_executable(
config,
compiler_version_or_requirement,
)
.await?;
let compiler_version = <P::Compiler as SolidityCompiler>::new(compiler_path.clone())
.version()
.await?;
let compiler_version = solc_version(compiler_version_or_requirement, config.wasm).await?;
let cache_key = CacheKey {
platform_key: P::config_id().to_string(),
@@ -77,8 +71,8 @@ impl CachedCompiler {
async move {
compile_contracts::<P>(
metadata.directory()?,
compiler_path,
metadata.files_to_compile()?,
config,
mode,
deployed_libraries,
)
@@ -138,8 +132,8 @@ impl CachedCompiler {
async fn compile_contracts<P: Platform>(
metadata_directory: impl AsRef<Path>,
compiler_path: impl AsRef<Path>,
mut files_to_compile: impl Iterator<Item = PathBuf>,
config: &Arguments,
mode: &Mode,
deployed_libraries: Option<&HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
) -> Result<CompilerOutput> {
@@ -149,6 +143,7 @@ async fn compile_contracts<P: Platform>(
.collect::<Vec<_>>();
Compiler::<P::Compiler>::new()
.with_solc_version_req(mode.version.clone())
.with_allow_path(metadata_directory)
// Handling the modes
.with_optimization(mode.optimize_setting)
@@ -172,7 +167,7 @@ async fn compile_contracts<P: Platform>(
compiler.with_library(path, ident.as_str(), *address)
})
})
.try_build(compiler_path)
.try_build(config)
.await
}
+12 -38
View File
@@ -14,8 +14,8 @@ use alloy::{
};
use anyhow::Context;
use clap::Parser;
use futures::StreamExt;
use futures::stream;
use futures::{Stream, StreamExt};
use indexmap::IndexMap;
use revive_dt_node_interaction::EthereumNode;
use temp_dir::TempDir;
@@ -163,7 +163,7 @@ where
{
let (report_tx, report_rx) = mpsc::unbounded_channel::<(Test<'_>, CaseResult)>();
let tests = prepare_tests::<L, F>(args, metadata_files);
let tests = prepare_tests::<L, F>(metadata_files);
let driver_task = start_driver_task::<L, F>(args, tests, span, report_tx).await?;
let status_reporter_task = start_reporter_task(report_rx);
@@ -172,17 +172,14 @@ where
Ok(())
}
fn prepare_tests<'a, L, F>(
args: &Arguments,
metadata_files: &'a [MetadataFile],
) -> impl Stream<Item = Test<'a>>
fn prepare_tests<'a, L, F>(metadata_files: &'a [MetadataFile]) -> impl Iterator<Item = Test<'a>>
where
L: Platform,
F: Platform,
L::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
F::Blockchain: revive_dt_node::Node + Send + Sync + 'static,
{
let filtered_tests = metadata_files
metadata_files
.iter()
.flat_map(|metadata_file| {
metadata_file
@@ -289,19 +286,12 @@ where
} else {
true
}
});
stream::iter(filtered_tests)
// Filter based on the compiler compatibility
.filter_map(move |test| async move {
let leader_support = does_compiler_support_mode::<L>(args, &test.mode)
.await
.ok()
.unwrap_or(false);
let follower_support = does_compiler_support_mode::<F>(args, &test.mode)
.await
.ok()
.unwrap_or(false);
})
.filter_map(move |test| {
let leader_support =
L::Compiler::supports_mode(test.mode.optimize_setting, test.mode.pipeline);
let follower_support =
F::Compiler::supports_mode(test.mode.optimize_setting, test.mode.pipeline);
let is_allowed = leader_support && follower_support;
if !is_allowed {
@@ -317,25 +307,9 @@ where
})
}
async fn does_compiler_support_mode<P: Platform>(
args: &Arguments,
mode: &Mode,
) -> anyhow::Result<bool> {
let compiler_version_or_requirement = mode.compiler_version_to_use(args.solc.clone());
let compiler_path =
P::Compiler::get_compiler_executable(args, compiler_version_or_requirement).await?;
let compiler_version = P::Compiler::new(compiler_path.clone()).version().await?;
Ok(P::Compiler::supports_mode(
&compiler_version,
mode.optimize_setting,
mode.pipeline,
))
}
async fn start_driver_task<'a, L, F>(
args: &Arguments,
tests: impl Stream<Item = Test<'a>>,
tests: impl Iterator<Item = Test<'a>>,
span: Span,
report_tx: mpsc::UnboundedSender<(Test<'a>, CaseResult)>,
) -> anyhow::Result<impl Future<Output = ()>>
@@ -356,7 +330,7 @@ where
.await?,
);
Ok(tests.for_each_concurrent(
Ok(stream::iter(tests).for_each_concurrent(
// We want to limit the concurrent tasks here because:
//
// 1. We don't want to overwhelm the nodes with too many requests, leading to responses timing out.
+31 -12
View File
@@ -7,6 +7,7 @@ use std::path::{Path, PathBuf};
use cache::get_or_download;
use download::SolcDownloader;
use semver::Version;
use revive_dt_common::types::VersionOrRequirement;
@@ -14,6 +15,25 @@ pub mod cache;
pub mod download;
pub mod list;
/// Return a [`SolcDownloader`] which can be used to download a `solc`
/// binary or return the resolved version it will download.
async fn downloader(
version: impl Into<VersionOrRequirement>,
wasm: bool,
) -> anyhow::Result<SolcDownloader> {
if wasm {
SolcDownloader::wasm(version).await
} else if cfg!(target_os = "linux") {
SolcDownloader::linux(version).await
} else if cfg!(target_os = "macos") {
SolcDownloader::macosx(version).await
} else if cfg!(target_os = "windows") {
SolcDownloader::windows(version).await
} else {
unimplemented!()
}
}
/// Downloads the solc binary for Wasm is `wasm` is set, otherwise for
/// the target platform.
///
@@ -24,17 +44,16 @@ pub async fn download_solc(
version: impl Into<VersionOrRequirement>,
wasm: bool,
) -> anyhow::Result<PathBuf> {
let downloader = if wasm {
SolcDownloader::wasm(version).await
} else if cfg!(target_os = "linux") {
SolcDownloader::linux(version).await
} else if cfg!(target_os = "macos") {
SolcDownloader::macosx(version).await
} else if cfg!(target_os = "windows") {
SolcDownloader::windows(version).await
} else {
unimplemented!()
}?;
let downloader = downloader(version, wasm).await?;
get_or_download(cache_directory, &downloader).await
}
/// Return the version of solc that will be downloaded via [`download_solc`]
/// given the version requirements and wasm flag.
pub async fn solc_version(
version: impl Into<VersionOrRequirement>,
wasm: bool,
) -> anyhow::Result<Version> {
let downloader = downloader(version, wasm).await?;
Ok(downloader.version.clone())
}