Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow multiple languages for compilers #128

Merged
merged 25 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ path = "tests/mocked.rs"
required-features = ["full", "project-util"]

[features]
default = ["rustls"]
default = ["rustls", "svm-solc"]

full = ["async", "svm-solc"]

Expand Down
2 changes: 1 addition & 1 deletion benches/compile_many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::path::Path;

fn compile_many_benchmark(c: &mut Criterion) {
let inputs = load_compiler_inputs();
let solc = SolcVersionManager::default().get_or_install(&Version::new(0, 8, 0)).unwrap();
let solc = Solc::find_or_install((&Version::new(0, 8, 0)).unwrap();

let mut group = c.benchmark_group("compile many");
group.sample_size(10);
Expand Down
75 changes: 63 additions & 12 deletions src/artifacts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
#![allow(ambiguous_glob_reexports)]

use crate::{
compile::*, error::SolcIoError, output::ErrorFilter, remappings::Remapping, utils,
ProjectPathsConfig, SolcError,
compile::*, compilers::solc::SolcLanguages, error::SolcIoError, output::ErrorFilter,
remappings::Remapping, utils, ProjectPathsConfig, SolcError,
};
use alloy_primitives::hex;
use md5::Digest;
use semver::Version;
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use std::{
collections::{BTreeMap, HashSet},
collections::{BTreeMap, HashMap, HashSet},
fmt, fs,
path::{Path, PathBuf},
str::FromStr,
Expand Down Expand Up @@ -49,18 +49,18 @@ pub type Contracts = FileToContractsMap<Contract>;
pub type Sources = BTreeMap<PathBuf, Source>;

/// A set of different Solc installations with their version and the sources to be compiled
pub(crate) type VersionedSources<C> = Vec<(C, Version, Sources)>;
pub(crate) type VersionedSources<L> = HashMap<L, HashMap<Version, Sources>>;

/// A set of different Solc installations with their version and the sources to be compiled
pub(crate) type VersionedFilteredSources<C> = Vec<(C, Version, FilteredSources)>;
pub(crate) type VersionedFilteredSources<L> = HashMap<L, HashMap<Version, FilteredSources>>;

pub const SOLIDITY: &str = "Solidity";
pub const YUL: &str = "Yul";

/// Input type `solc` expects.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SolcInput {
pub language: String,
pub language: SolcLanguages,
pub sources: Sources,
pub settings: Settings,
}
Expand All @@ -69,14 +69,49 @@ pub struct SolcInput {
impl Default for SolcInput {
fn default() -> Self {
SolcInput {
language: SOLIDITY.to_string(),
language: SolcLanguages::Solidity,
sources: Sources::default(),
settings: Settings::default(),
}
}
}

impl SolcInput {
pub fn new(language: SolcLanguages, sources: Sources, mut settings: Settings) -> Self {
if language == SolcLanguages::Yul {
if !settings.remappings.is_empty() {
warn!("omitting remappings supplied for the yul sources");
settings.remappings = vec![];
}
}
Self { language, sources, settings }
}

pub fn resolve_and_build(sources: Sources, settings: Settings) -> Vec<Self> {
mattsse marked this conversation as resolved.
Show resolved Hide resolved
let mut solidity_sources = BTreeMap::new();
let mut yul_sources = BTreeMap::new();

for (file, source) in sources {
if file.extension().map_or(false, |e| e == "yul") {
yul_sources.insert(file, source);
} else if file.extension().map_or(false, |e| e == "sol") {
solidity_sources.insert(file, source);
}
}

let mut res = Vec::new();

if !solidity_sources.is_empty() {
res.push(SolcInput::new(SolcLanguages::Solidity, solidity_sources, settings.clone()))
}

if !yul_sources.is_empty() {
res.push(SolcInput::new(SolcLanguages::Yul, yul_sources, settings))
}

res
}

/// This will remove/adjust values in the [`SolcInput`] that are not compatible with this
/// version
pub fn sanitize(&mut self, version: &Version) {
Expand Down Expand Up @@ -125,7 +160,19 @@ impl SolcInput {
/// The flag indicating whether the current [SolcInput] is
/// constructed for the yul sources
pub fn is_yul(&self) -> bool {
self.language == YUL
self.language == SolcLanguages::Yul
}

pub fn with_remappings(mut self, remappings: Vec<Remapping>) -> Self {
if self.language == SolcLanguages::Yul {
if !remappings.is_empty() {
warn!("omitting remappings supplied for the yul sources");
}
} else {
self.settings.remappings = remappings;
}

self
}
}

Expand All @@ -137,7 +184,7 @@ impl SolcInput {
/// the verified contracts
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct StandardJsonCompilerInput {
pub language: String,
pub language: SolcLanguages,
#[serde(with = "serde_helpers::tuple_vec_map")]
pub sources: Vec<(PathBuf, Source)>,
pub settings: Settings,
Expand All @@ -147,7 +194,7 @@ pub struct StandardJsonCompilerInput {

impl StandardJsonCompilerInput {
pub fn new(sources: Vec<(PathBuf, Source)>, settings: Settings) -> Self {
Self { language: SOLIDITY.to_string(), sources, settings }
Self { language: SolcLanguages::Solidity, sources, settings }
}

/// Normalizes the EVM version used in the settings to be up to the latest one
Expand Down Expand Up @@ -297,6 +344,10 @@ impl Settings {
model_checker.show_unsupported = None;
}
}

if let Some(ref mut evm_version) = self.evm_version {
self.evm_version = evm_version.normalize_version_solc(version);
}
}

/// Inserts a set of `ContractOutputSelection`
Expand Down Expand Up @@ -2121,7 +2172,7 @@ mod tests {
let settings = Settings { metadata: Some(BytecodeHash::Ipfs.into()), ..Default::default() };

let input =
SolcInput { language: "Solidity".to_string(), sources: Default::default(), settings };
SolcInput { language: SolcLanguages::Solidity, sources: Default::default(), settings };

let i = input.clone().sanitized(&version);
assert_eq!(i.settings.metadata.unwrap().bytecode_hash, Some(BytecodeHash::Ipfs));
Expand All @@ -2141,7 +2192,7 @@ mod tests {
};

let input =
SolcInput { language: "Solidity".to_string(), sources: Default::default(), settings };
SolcInput { language: SolcLanguages::Solidity, sources: Default::default(), settings };

let i = input.clone().sanitized(&version);
assert_eq!(i.settings.metadata.unwrap().cbor_metadata, Some(true));
Expand Down
3 changes: 1 addition & 2 deletions src/buildinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,9 @@ mod tests {

#[test]
fn build_info_serde() {
let inputs = SolcInput::build(
let inputs = SolcInput::resolve_and_build(
BTreeMap::from([(PathBuf::from("input.sol"), Source::new(""))]),
Default::default(),
&Version::new(0, 8, 4),
);
let output = CompilerOutput::<Error>::default();
let v: Version = "0.8.4+commit.c7e474f2".parse().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl<S: CompilerSettings> CompilerCache<S> {
/// let cache = SolFilesCache::read_joined(&project.paths)?;
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub fn read_joined<C>(paths: &ProjectPathsConfig<C>) -> Result<Self> {
pub fn read_joined<L>(paths: &ProjectPathsConfig<L>) -> Result<Self> {
let mut cache = CompilerCache::read(&paths.cache)?;
cache.join_entries(&paths.root).join_artifacts_files(&paths.artifacts);
Ok(cache)
Expand Down
40 changes: 26 additions & 14 deletions src/compile/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{
artifacts::Source,
compilers::CompilerInput,
error::{Result, SolcError},
resolver::parse::SolData,
utils, CompilerOutput, SolcInput,
Expand Down Expand Up @@ -317,6 +316,9 @@ impl Solc {
pub fn blocking_install(version: &Version) -> std::result::Result<Self, svm::SvmError> {
use crate::utils::RuntimeOrHandle;

#[cfg(test)]
crate::take_solc_installer_lock!(_lock);

trace!("blocking installing solc version \"{}\"", version);
crate::report::solc_installation_start(version);
// The async version `svm::install` is used instead of `svm::blocking_intsall`
Expand Down Expand Up @@ -387,11 +389,13 @@ impl Solc {
let path = path.as_ref();
let mut res: CompilerOutput = Default::default();
for input in
SolcInput::build(Source::read_sol_yul_from(path)?, Default::default(), &self.version)
SolcInput::resolve_and_build(Source::read_sol_yul_from(path)?, Default::default())
{
let input = input.sanitized(&self.version);
let output = self.compile(&input)?;
res.merge(output)
}

Ok(res)
}

Expand Down Expand Up @@ -518,16 +522,25 @@ impl Solc {

cmd
}

pub fn find_or_install(version: &Version) -> Result<Self> {
let solc = if let Some(solc) = Self::find_svm_installed_version(version.to_string())? {
solc
} else {
Self::blocking_install(version)?
};

Ok(solc)
}
}

#[cfg(feature = "async")]
impl Solc {
/// Convenience function for compiling all sources under the given path
pub async fn async_compile_source(&self, path: impl AsRef<Path>) -> Result<CompilerOutput> {
self.async_compile(&SolcInput::build(
self.async_compile(&SolcInput::resolve_and_build(
Source::async_read_all_from(path, SOLC_EXTENSIONS).await?,
Default::default(),
&self.version,
))
.await
}
Expand Down Expand Up @@ -629,11 +642,7 @@ impl AsRef<Path> for Solc {
#[cfg(feature = "svm-solc")]
mod tests {
use super::*;
use crate::{
compilers::{solc::SolcVersionManager, CompilerVersionManager, VersionManagerError},
resolver::parse::SolData,
Artifact,
};
use crate::{resolver::parse::SolData, Artifact};

#[test]
fn test_version_parse() {
Expand All @@ -643,7 +652,11 @@ mod tests {
}

fn solc() -> Solc {
SolcVersionManager::default().get_or_install(&Version::new(0, 8, 18)).unwrap()
if let Some(solc) = Solc::find_svm_installed_version("0.8.18").unwrap() {
solc
} else {
Solc::blocking_install(&Version::new(0, 8, 18)).unwrap()
}
}

#[test]
Expand Down Expand Up @@ -758,7 +771,7 @@ mod tests {
Solc::blocking_install(&version).unwrap();
}
drop(_lock);
let res = SolcVersionManager::default().get_installed(&version).unwrap();
let res = Solc::find_svm_installed_version(ver).unwrap().unwrap();
let expected = svm::data_dir().join(ver).join(format!("solc-{ver}"));
assert_eq!(res.solc, expected);
}
Expand All @@ -775,8 +788,7 @@ mod tests {
#[test]
fn does_not_find_not_installed_version() {
let ver = "1.1.1";
let version = Version::from_str(ver).unwrap();
let res = SolcVersionManager::default().get_installed(&version);
assert!(matches!(res, Err(VersionManagerError::VersionNotInstalled(_))));
let res = Solc::find_svm_installed_version(ver).unwrap();
assert!(matches!(res, None));
}
}
Loading
Loading