diff --git a/Cargo.lock b/Cargo.lock index 0e7bd7cf0d39..375391847d28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3257,9 +3257,9 @@ dependencies = [ [[package]] name = "foundry-block-explorers" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7101c78b700b8c84294aee0319289af724201181a9b757d8a9a2fea991f3ce4e" +checksum = "673c42208fee48238ef6833cf55295c9e9b5546383caf426da72d849fb43dc24" dependencies = [ "alloy-chains", "alloy-json-abi", @@ -3412,9 +3412,9 @@ dependencies = [ [[package]] name = "foundry-compilers" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd571882cd635dd2a11169d5b696e51644fc803487989e2b5fd7ff6bdd008f1c" +checksum = "fe70a3860ec9f1861e5d82cbd4ffc55756975c0826dacf8ae4d6d696df8f7f53" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -3425,7 +3425,7 @@ dependencies = [ "fs_extra", "futures-util", "home", - "itertools 0.12.1", + "itertools 0.13.0", "md-5", "memmap2 0.9.4", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index d9db1d72f1e8..3818778fa02e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,8 +137,8 @@ foundry-wallets = { path = "crates/wallets" } foundry-linking = { path = "crates/linking" } # solc & compilation utilities -foundry-block-explorers = { version = "0.2.8", default-features = false } -foundry-compilers = { version = "0.5.2", default-features = false } +foundry-block-explorers = { version = "0.3.0", default-features = false } +foundry-compilers = { version = "0.6.0", default-features = false } ## revm # no default features to avoid c-kzg diff --git a/crates/cast/bin/cmd/storage.rs b/crates/cast/bin/cmd/storage.rs index a59d93c5d104..aedf8447190c 100644 --- a/crates/cast/bin/cmd/storage.rs +++ b/crates/cast/bin/cmd/storage.rs @@ -20,15 +20,15 @@ use foundry_common::{ }; use foundry_compilers::{ artifacts::StorageLayout, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, - Artifact, CompilerConfig, ConfigurableContractArtifact, Project, + compilers::{solc::SolcCompiler, Compiler, CompilerSettings}, + Artifact, ConfigurableContractArtifact, Project, Solc, }; use foundry_config::{ figment::{self, value::Dict, Metadata, Profile}, impl_figment_convert_cast, Config, }; use semver::Version; -use std::{str::FromStr, sync::Arc}; +use std::str::FromStr; /// The minimum Solc version for outputting storage layouts. /// @@ -142,11 +142,10 @@ impl StorageArgs { let mut project = etherscan_project(metadata, root_path)?; add_storage_layout_output(&mut project); - let vm = SolcVersionManager::default(); - project.compiler_config = if auto_detect { - CompilerConfig::AutoDetect(Arc::new(vm)) + project.compiler = if auto_detect { + SolcCompiler::AutoDetect } else { - CompilerConfig::Specific(vm.get_or_install(&version)?) + SolcCompiler::Specific(Solc::find_or_install(&version)?) }; // Compile @@ -160,8 +159,8 @@ impl StorageArgs { if is_storage_layout_empty(&artifact.storage_layout) && auto_detect { // try recompiling with the minimum version eprintln!("The requested contract was compiled with {version} while the minimum version for storage layouts is {MIN_SOLC} and as a result the output may be empty."); - let solc = SolcVersionManager::default().get_or_install(&MIN_SOLC)?; - project.compiler_config = CompilerConfig::Specific(solc); + let solc = Solc::find_or_install(&MIN_SOLC)?; + project.compiler = SolcCompiler::Specific(solc); if let Ok(output) = ProjectCompiler::new().quiet(true).compile(&project) { out = output; let (_, new_artifact) = out @@ -284,10 +283,15 @@ fn print_storage(layout: StorageLayout, values: Vec, pretty: bool) Ok(()) } -fn add_storage_layout_output(project: &mut Project) { +fn add_storage_layout_output(project: &mut Project) { project.artifacts.additional_values.storage_layout = true; - let output_selection = project.artifacts.output_selection(); - project.settings.push_all(output_selection); + project.settings.update_output_selection(|selection| { + selection.0.values_mut().for_each(|contract_selection| { + contract_selection + .values_mut() + .for_each(|selection| selection.push("storageLayout".to_string())) + }); + }) } fn is_storage_layout_empty(storage_layout: &Option) -> bool { diff --git a/crates/cheatcodes/src/fs.rs b/crates/cheatcodes/src/fs.rs index 308d86641306..fd4234d21fa0 100644 --- a/crates/cheatcodes/src/fs.rs +++ b/crates/cheatcodes/src/fs.rs @@ -283,7 +283,7 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result = - Lazy::new(|| SolcVersionManager::default().get_or_install(&Version::new(0, 8, 19)).unwrap()); +static SOLC: Lazy = Lazy::new(|| Solc::find_or_install(&Version::new(0, 8, 19)).unwrap()); /// Benchmark for the `clone_with_new_line` function in [SessionSource] fn clone_with_new_line(c: &mut Criterion) { diff --git a/crates/chisel/src/executor.rs b/crates/chisel/src/executor.rs index 3ce6a7d19976..eea595486dfa 100644 --- a/crates/chisel/src/executor.rs +++ b/crates/chisel/src/executor.rs @@ -1401,11 +1401,7 @@ impl<'a> Iterator for InstructionIter<'a> { #[cfg(test)] mod tests { use super::*; - use foundry_compilers::{ - compilers::{solc::SolcVersionManager, CompilerVersionManager}, - error::SolcError, - Solc, - }; + use foundry_compilers::{error::SolcError, Solc}; use semver::Version; use std::sync::Mutex; @@ -1690,8 +1686,7 @@ mod tests { for _ in 0..3 { let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap(); if !*is_preinstalled { - let solc = SolcVersionManager::default() - .get_or_install(&version.parse().unwrap()) + let solc = Solc::find_or_install(&version.parse().unwrap()) .map(|solc| (solc.version.clone(), solc)); match solc { Ok((v, solc)) => { @@ -1712,9 +1707,7 @@ mod tests { } } - let solc = SolcVersionManager::default() - .get_or_install(&Version::new(0, 8, 19)) - .expect("could not install solc"); + let solc = Solc::find_or_install(&Version::new(0, 8, 19)).expect("could not install solc"); SessionSource::new(solc, Default::default()) } diff --git a/crates/chisel/src/session_source.rs b/crates/chisel/src/session_source.rs index 21fa3d834576..6882a63aa3e4 100644 --- a/crates/chisel/src/session_source.rs +++ b/crates/chisel/src/session_source.rs @@ -8,7 +8,6 @@ use eyre::Result; use forge_fmt::solang_ext::SafeUnwrap; use foundry_compilers::{ artifacts::{Settings, Source, Sources}, - compilers::{solc::SolcVersionManager, CompilerInput, CompilerVersionManager}, CompilerOutput, Solc, SolcInput, }; use foundry_config::{Config, SolcReq}; @@ -104,8 +103,6 @@ impl SessionSourceConfig { SolcReq::Version(Version::new(0, 8, 19)) }; - let vm = SolcVersionManager::default(); - match solc_req { SolcReq::Version(version) => { // Validate that the requested evm version is supported by the solc version @@ -118,15 +115,16 @@ impl SessionSourceConfig { } } - let solc = if let Ok(solc) = vm.get_installed(&version) { - solc - } else { - if self.foundry_config.offline { - eyre::bail!("can't install missing solc {version} in offline mode") - } - println!("{}", format!("Installing solidity version {version}...").green()); - vm.install(&version)? - }; + let solc = + if let Some(solc) = Solc::find_svm_installed_version(version.to_string())? { + solc + } else { + if self.foundry_config.offline { + eyre::bail!("can't install missing solc {version} in offline mode") + } + println!("{}", format!("Installing solidity version {version}...").green()); + Solc::blocking_install(&version)? + }; Ok(solc) } SolcReq::Local(solc) => { @@ -329,9 +327,10 @@ impl SessionSource { }; // we only care about the solidity source, so we can safely unwrap - SolcInput::build(sources, settings, &self.solc.version) + SolcInput::resolve_and_build(sources, settings) .into_iter() .next() + .map(|i| i.sanitized(&self.solc.version)) .expect("Solidity source not found") } diff --git a/crates/cli/src/opts/build/core.rs b/crates/cli/src/opts/build/core.rs index 99caaea35454..85575bccce57 100644 --- a/crates/cli/src/opts/build/core.rs +++ b/crates/cli/src/opts/build/core.rs @@ -3,7 +3,8 @@ use crate::{opts::CompilerArgs, utils::LoadConfig}; use clap::{Parser, ValueHint}; use eyre::Result; use foundry_compilers::{ - artifacts::RevertStrings, remappings::Remapping, utils::canonicalized, Project, + artifacts::RevertStrings, compilers::multi::MultiCompiler, remappings::Remapping, + utils::canonicalized, Project, }; use foundry_config::{ figment, @@ -132,7 +133,7 @@ impl CoreBuildArgs { /// This loads the `foundry_config::Config` for the current workspace (see /// [`utils::find_project_root_path`] and merges the cli `BuildArgs` into it before returning /// [`foundry_config::Config::project()`] - pub fn project(&self) -> Result { + pub fn project(&self) -> Result> { let config = self.try_load_config_emit_warnings()?; Ok(config.project()?) } diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index 329e53213d90..8e8cff36854d 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -6,15 +6,11 @@ use eyre::{Context, Result}; use foundry_block_explorers::contract::Metadata; use foundry_compilers::{ artifacts::{BytecodeObject, ContractBytecodeSome, Libraries}, - compilers::{ - solc::SolcVersionManager, vyper::parser::VyperParsedSource, Compiler, - CompilerVersionManager, - }, + compilers::{solc::SolcCompiler, Compiler}, remappings::Remapping, report::{BasicStdoutReporter, NoReporter, Report}, - resolver::{parse::SolData, GraphEdges}, - Artifact, ArtifactId, CompilerConfig, FileFilter, Project, ProjectCompileOutput, - ProjectPathsConfig, SolcConfig, SolcSparseFileFilter, SparseOutputFileFilter, + Artifact, ArtifactId, FileFilter, Project, ProjectBuilder, ProjectCompileOutput, + ProjectPathsConfig, Solc, SolcConfig, SparseOutputFileFilter, }; use foundry_linking::Linker; use num_format::{Locale, ToFormattedString}; @@ -521,7 +517,10 @@ pub async fn compile_from_source( } /// Creates a [Project] from an Etherscan source. -pub fn etherscan_project(metadata: &Metadata, target_path: impl AsRef) -> Result { +pub fn etherscan_project( + metadata: &Metadata, + target_path: impl AsRef, +) -> Result> { let target_path = dunce::canonicalize(target_path.as_ref())?; let sources_path = target_path.join(&metadata.contract_name); metadata.source_tree().write_to(&target_path)?; @@ -553,17 +552,16 @@ pub fn etherscan_project(metadata: &Metadata, target_path: impl AsRef) -> .build_with_root(sources_path); let v = metadata.compiler_version()?; - let vm = SolcVersionManager::default(); - let solc = vm.get_or_install(&v)?; + let solc = Solc::find_or_install(&v)?; - let compiler_config = CompilerConfig::Specific(solc); + let compiler = SolcCompiler::Specific(solc); - Ok(Project::builder() + Ok(ProjectBuilder::::default() .settings(SolcConfig::builder().settings(settings).build().settings) .paths(paths) .ephemeral() .no_artifacts() - .build(compiler_config)?) + .build(compiler)?) } /// Bundles multiple `SkipBuildFilter` into a single `FileFilter` @@ -595,22 +593,6 @@ impl FileFilter for &SkipBuildFilters { } } -impl SparseOutputFileFilter for SkipBuildFilters { - fn sparse_sources(&self, file: &Path, graph: &GraphEdges) -> Vec { - SolcSparseFileFilter::new(self).sparse_sources(file, graph) - } -} - -impl SparseOutputFileFilter for SkipBuildFilters { - fn sparse_sources(&self, file: &Path, _graph: &GraphEdges) -> Vec { - if self.is_match(file) { - vec![file.to_path_buf()] - } else { - vec![] - } - } -} - impl SkipBuildFilters { /// Creates a new `SkipBuildFilters` from multiple `SkipBuildFilter`. pub fn new( diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index f2957c6a4ce6..cc2cfc1b6d2d 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -22,14 +22,14 @@ use foundry_compilers::{ }, cache::SOLIDITY_FILES_CACHE_FILENAME, compilers::{ - solc::SolcVersionManager, + multi::{MultiCompiler, MultiCompilerSettings}, + solc::SolcCompiler, vyper::{Vyper, VyperSettings}, - Compiler, CompilerVersionManager, + Compiler, }, error::SolcError, remappings::{RelativeRemapping, Remapping}, - CompilerConfig, ConfigurableArtifacts, EvmVersion, Project, ProjectBuilder, ProjectPathsConfig, - Solc, SolcConfig, + ConfigurableArtifacts, EvmVersion, Project, ProjectPathsConfig, Solc, }; use inflector::Inflector; use regex::Regex; @@ -42,7 +42,6 @@ use std::{ fs, path::{Path, PathBuf}, str::FromStr, - sync::Arc, }; // Macros useful for creating a figment. @@ -193,6 +192,8 @@ pub struct Config { /// **Note** for backwards compatibility reasons this also accepts solc_version from the toml /// file, see [`BackwardsCompatProvider`] pub solc: Option, + /// The Vyper instance to use if any. + pub vyper: Option, /// whether to autodetect the solc compiler version to use pub auto_detect_solc: bool, /// Offline mode, if set, network access (downloading solc) is disallowed. @@ -765,55 +766,22 @@ impl Config { /// let config = Config::load_with_root(".").sanitized(); /// let project = config.project(); /// ``` - pub fn project(&self) -> Result { + pub fn project(&self) -> Result, SolcError> { self.create_project(self.cache, false) } - /// Configures [Project] with [Vyper] compiler. - pub fn vyper_project(&self) -> Result, SolcError> { - self.create_vyper_project(self.cache, false) - } - /// Same as [`Self::project()`] but sets configures the project to not emit artifacts and ignore /// cache. - pub fn ephemeral_no_artifacts_project(&self) -> Result { + pub fn ephemeral_no_artifacts_project(&self) -> Result, SolcError> { self.create_project(false, true) } /// Creates a [Project] with the given `cached` and `no_artifacts` flags pub fn create_project(&self, cached: bool, no_artifacts: bool) -> Result { - let compiler_config = self.solc_config()?; - let settings = SolcConfig::builder().settings(self.solc_settings()?).build().settings; - - self.create_project_with_compiler(cached, no_artifacts, compiler_config, settings) - } - - /// Creates a [Project] with the given `cached` and `no_artifacts` flags - pub fn create_vyper_project( - &self, - cached: bool, - no_artifacts: bool, - ) -> Result, SolcError> { - self.create_project_with_compiler( - cached, - no_artifacts, - self.vyper_config()?, - self.vyper_settings()?, - ) - } - - /// Creates a [Project] with a given [CompilerConfig]. - pub fn create_project_with_compiler( - &self, - cached: bool, - no_artifacts: bool, - compiler_config: CompilerConfig, - settings: C::Settings, - ) -> Result, SolcError> { - let project = ProjectBuilder::::new(Default::default()) + let project = Project::builder() .artifacts(self.configured_artifacts_handler()) .paths(self.project_paths()) - .settings(settings) + .settings(self.compiler_settings()?) .ignore_error_codes(self.ignored_error_codes.iter().copied().map(Into::into)) .ignore_paths(self.ignored_file_paths.clone()) .set_compiler_severity_filter(if self.deny_warnings { @@ -825,7 +793,7 @@ impl Config { .set_cached(cached && !self.build_info) .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts) - .build(compiler_config)?; + .build(self.compiler()?)?; if self.force { self.cleanup(&project)?; @@ -861,10 +829,9 @@ impl Config { /// If `solc` is [`SolcReq::Local`] then this will ensure that the path exists. fn ensure_solc(&self) -> Result, SolcError> { if let Some(ref solc) = self.solc { - let version_manager = SolcVersionManager::default(); let solc = match solc { SolcReq::Version(version) => { - if let Ok(solc) = version_manager.get_installed(version) { + if let Some(solc) = Solc::find_svm_installed_version(version.to_string())? { solc } else { if self.offline { @@ -872,7 +839,7 @@ impl Config { "can't install missing solc {version} in offline mode" ))) } - version_manager.install(version)? + Solc::blocking_install(version)? } } SolcReq::Local(solc) => { @@ -953,21 +920,33 @@ impl Config { } /// Returns configuration for a compiler to use when setting up a [Project]. - pub fn solc_config(&self) -> Result, SolcError> { + pub fn solc_compiler(&self) -> Result { if let Some(solc) = self.ensure_solc()? { - Ok(CompilerConfig::Specific(solc)) + Ok(SolcCompiler::Specific(solc)) } else { - Ok(CompilerConfig::AutoDetect(Arc::new(SolcVersionManager::default()))) + Ok(SolcCompiler::AutoDetect) } } - /// Returns configuration for a compiler to use when setting up a [Project]. - pub fn vyper_config(&self) -> Result, SolcError> { - if let Some(SolcReq::Local(path)) = &self.solc { - Ok(CompilerConfig::Specific(Vyper::new(path)?)) + /// Returns configured [Vyper] compiler. + pub fn vyper_compiler(&self) -> Result, SolcError> { + let vyper = if let Some(path) = &self.vyper { + Some(Vyper::new(path)?) } else { - Ok(CompilerConfig::Specific(Vyper::new("vyper")?)) - } + Vyper::new("vyper").ok() + }; + + Ok(vyper) + } + + /// Returns configuration for a compiler to use when setting up a [Project]. + pub fn compiler(&self) -> Result { + Ok(MultiCompiler { solc: self.solc_compiler()?, vyper: self.vyper_compiler()? }) + } + + /// Returns configured [MultiCompilerSettings]. + pub fn compiler_settings(&self) -> Result { + Ok(MultiCompilerSettings { solc: self.solc_settings()?, vyper: self.vyper_settings()? }) } /// Returns all configured [`Remappings`] @@ -2017,6 +1996,7 @@ impl Default for Config { gas_reports: vec!["*".to_string()], gas_reports_ignore: vec![], solc: None, + vyper: None, auto_detect_solc: true, offline: false, optimizer: true, @@ -2782,23 +2762,6 @@ fn canonic(path: impl Into) -> PathBuf { foundry_compilers::utils::canonicalize(&path).unwrap_or(path) } -/// Executes the given closure with a [Project] configured via the given [Config]. -#[macro_export] -macro_rules! with_resolved_project { - ($config:ident, |$prj:ident| $e:expr) => { - match $config.lang { - foundry_config::Language::Solidity => { - let $prj = $config.project(); - $e - } - foundry_config::Language::Vyper => { - let $prj = $config.vyper_project(); - $e - } - } - }; -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/forge/bin/cmd/build.rs b/crates/forge/bin/cmd/build.rs index c4bbbb8a21b3..c2fc8088f469 100644 --- a/crates/forge/bin/cmd/build.rs +++ b/crates/forge/bin/cmd/build.rs @@ -3,9 +3,7 @@ use clap::Parser; use eyre::Result; use foundry_cli::{opts::CoreBuildArgs, utils::LoadConfig}; use foundry_common::compile::{ProjectCompiler, SkipBuildFilter, SkipBuildFilters}; -use foundry_compilers::{ - compilers::Compiler, Project, ProjectCompileOutput, SparseOutputFileFilter, -}; +use foundry_compilers::{Project, ProjectCompileOutput}; use foundry_config::{ figment::{ self, @@ -13,7 +11,7 @@ use foundry_config::{ value::{Dict, Map, Value}, Metadata, Profile, Provider, }, - with_resolved_project, Config, + Config, }; use serde::Serialize; use watchexec::config::{InitConfig, RuntimeConfig}; @@ -77,7 +75,7 @@ pub struct BuildArgs { } impl BuildArgs { - pub fn run(self) -> Result<()> { + pub fn run(self) -> Result { let mut config = self.try_load_config_emit_warnings()?; if install::install_missing_dependencies(&mut config, self.args.silent) && @@ -87,43 +85,20 @@ impl BuildArgs { config = self.load_config(); } - with_resolved_project!(config, |project| { - let project = project?; - - let filter = if let Some(ref skip) = self.skip { - if !skip.is_empty() { - let filter = SkipBuildFilters::new(skip.clone(), project.root().clone())?; - Some(filter) - } else { - None - } - } else { - None - }; - - self.run_with_project(project, filter)?; - }); + let project = config.project()?; - Ok(()) - } - - pub fn run_with_project( - &self, - project: Project, - filter: Option + 'static>, - ) -> Result> - where - C::CompilationError: Clone, - { let mut compiler = ProjectCompiler::new() .print_names(self.names) .print_sizes(self.sizes) .quiet(self.format_json) .bail(!self.format_json); - if let Some(filter) = filter { - compiler = compiler.filter(Box::new(filter)); - } + if let Some(ref skip) = self.skip { + if !skip.is_empty() { + let filter = SkipBuildFilters::new(skip.clone(), project.root().clone())?; + compiler = compiler.filter(Box::new(filter)); + } + }; let output = compiler.compile(&project)?; diff --git a/crates/forge/bin/cmd/coverage.rs b/crates/forge/bin/cmd/coverage.rs index 22dbab67c568..2fcfbb1dcb6e 100644 --- a/crates/forge/bin/cmd/coverage.rs +++ b/crates/forge/bin/cmd/coverage.rs @@ -97,7 +97,7 @@ impl CoverageArgs { /// Builds the project. fn build(&self, config: &Config) -> Result<(Project, ProjectCompileOutput)> { // Set up the project - let mut project = config.ephemeral_no_artifacts_project()?; + let mut project = config.create_project(false, false)?; if self.ir_minimum { // TODO: How to detect solc version if the user does not specify a solc version in // config case1: specify local installed solc ? @@ -124,12 +124,12 @@ impl CoverageArgs { // https://github.com/ethereum/solidity/issues/12533#issuecomment-1013073350 // And also in new releases of solidity: // https://github.com/ethereum/solidity/issues/13972#issuecomment-1628632202 - project.settings = project.settings.with_via_ir_minimum_optimization() + project.settings.solc = project.settings.solc.with_via_ir_minimum_optimization() } else { - project.settings.optimizer.disable(); - project.settings.optimizer.runs = None; - project.settings.optimizer.details = None; - project.settings.via_ir = None; + project.settings.solc.optimizer.disable(); + project.settings.solc.optimizer.runs = None; + project.settings.solc.optimizer.details = None; + project.settings.solc.via_ir = None; } let output = ProjectCompiler::default() diff --git a/crates/forge/bin/cmd/flatten.rs b/crates/forge/bin/cmd/flatten.rs index c1351d06d95a..0c9c6f23ae8b 100644 --- a/crates/forge/bin/cmd/flatten.rs +++ b/crates/forge/bin/cmd/flatten.rs @@ -5,7 +5,7 @@ use foundry_cli::{ utils::LoadConfig, }; use foundry_common::{compile::compile_target, fs}; -use foundry_compilers::{error::SolcError, flatten::Flattener}; +use foundry_compilers::{compilers::solc::SolcLanguage, error::SolcError, flatten::Flattener}; use std::path::PathBuf; /// CLI arguments for `forge flatten`. @@ -39,7 +39,7 @@ impl FlattenArgs { let mut config = build_args.try_load_config_emit_warnings()?; // `Flattener` uses the typed AST for better flattening results. config.ast = true; - let project = config.ephemeral_no_artifacts_project()?; + let project = config.create_project(false, true)?; let target_path = dunce::canonicalize(target_path)?; let compiler_output = compile_target(&target_path, &project, false); @@ -52,7 +52,7 @@ impl FlattenArgs { // Fallback to the old flattening implementation if we couldn't compile the target // successfully. This would be the case if the target has invalid // syntax. (e.g. Solang) - project.paths.flatten(&target_path) + project.paths.clone().with_language::().flatten(&target_path) } } .map_err(|err: SolcError| eyre::eyre!("Failed to flatten: {err}"))?; diff --git a/crates/forge/bin/cmd/fmt.rs b/crates/forge/bin/cmd/fmt.rs index 71ac7ceb0083..b3207bcb754e 100644 --- a/crates/forge/bin/cmd/fmt.rs +++ b/crates/forge/bin/cmd/fmt.rs @@ -3,7 +3,7 @@ use eyre::Result; use forge_fmt::{format_to, parse, print_diagnostics_report}; use foundry_cli::utils::{FoundryPathExt, LoadConfig}; use foundry_common::{fs, glob::expand_globs, term::cli_warn}; -use foundry_compilers::{Solc, SOLC_EXTENSIONS}; +use foundry_compilers::{compilers::solc::SolcLanguage, SOLC_EXTENSIONS}; use foundry_config::impl_figment_convert_basic; use rayon::prelude::*; use similar::{ChangeTag, TextDiff}; @@ -60,7 +60,7 @@ impl FmtArgs { [] => { // Retrieve the project paths, and filter out the ignored ones. let project_paths: Vec = config - .project_paths::() + .project_paths::() .input_files_iter() .filter(|p| !(ignored.contains(p) || ignored.contains(&cwd.join(p)))) .collect(); diff --git a/crates/forge/bin/cmd/geiger/mod.rs b/crates/forge/bin/cmd/geiger/mod.rs index 0b08f2d0d76e..451c29dbd792 100644 --- a/crates/forge/bin/cmd/geiger/mod.rs +++ b/crates/forge/bin/cmd/geiger/mod.rs @@ -1,7 +1,7 @@ use clap::{Parser, ValueHint}; use eyre::{Result, WrapErr}; use foundry_cli::utils::LoadConfig; -use foundry_compilers::{resolver::parse::SolData, Graph, Solc}; +use foundry_compilers::{resolver::parse::SolData, Graph}; use foundry_config::{impl_figment_convert_basic, Config}; use itertools::Itertools; use rayon::prelude::*; @@ -62,7 +62,7 @@ impl GeigerArgs { let mut sources: Vec = { if self.paths.is_empty() { - Graph::::resolve(&config.project_paths::())? + Graph::::resolve(&config.project_paths())? .files() .keys() .cloned() diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index 03d6266d71f5..a441b8e6651b 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -20,8 +20,9 @@ use foundry_common::{ shell, }; use foundry_compilers::{ - artifacts::output_selection::OutputSelection, utils::source_files_iter, SolcSparseFileFilter, - SOLC_EXTENSIONS, + artifacts::output_selection::OutputSelection, + compilers::{multi::MultiCompilerLanguage, CompilerSettings, Language}, + utils::source_files_iter, }; use foundry_config::{ figment, @@ -157,10 +158,11 @@ impl TestArgs { filter: &ProjectPathsAwareFilter, ) -> Result> { let mut project = config.create_project(true, true)?; - project.settings.output_selection = - OutputSelection::common_output_selection(["abi".to_string()]); + project.settings.update_output_selection(|selection| { + *selection = OutputSelection::common_output_selection(["abi".to_string()]); + }); - let output = project.compile_sparse(Box::new(SolcSparseFileFilter::new(filter.clone())))?; + let output = project.compile_sparse(Box::new(filter.clone()))?; if output.has_compiler_errors() { println!("{}", output); @@ -210,7 +212,10 @@ impl TestArgs { } // Always recompile all sources to ensure that `getCode` cheatcode can use any artifact. - test_sources.extend(source_files_iter(project.paths.sources, SOLC_EXTENSIONS)); + test_sources.extend(source_files_iter( + project.paths.sources, + MultiCompilerLanguage::FILE_EXTENSIONS, + )); Ok(test_sources) } diff --git a/crates/forge/bin/cmd/tree.rs b/crates/forge/bin/cmd/tree.rs index e2f958034f1b..088975d8752e 100644 --- a/crates/forge/bin/cmd/tree.rs +++ b/crates/forge/bin/cmd/tree.rs @@ -3,7 +3,7 @@ use eyre::Result; use foundry_cli::{opts::ProjectPathsArgs, utils::LoadConfig}; use foundry_compilers::{ resolver::{parse::SolData, Charset, TreeOptions}, - Graph, Solc, + Graph, }; /// CLI arguments for `forge tree`. @@ -28,7 +28,7 @@ foundry_config::impl_figment_convert!(TreeArgs, opts); impl TreeArgs { pub fn run(self) -> Result<()> { let config = self.try_load_config_emit_warnings()?; - let graph = Graph::::resolve(&config.project_paths::())?; + let graph = Graph::::resolve(&config.project_paths())?; let opts = TreeOptions { charset: self.charset, no_dedupe: self.no_dedupe }; graph.print_with_options(opts); diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 4e671a77a630..8508abbe55b7 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -313,10 +313,10 @@ impl MultiContractRunnerBuilder { /// Given an EVM, proceeds to return a runner which is able to execute all tests /// against that evm - pub fn build( + pub fn build( self, root: &Path, - output: ProjectCompileOutput, + output: ProjectCompileOutput, env: revm::primitives::Env, evm_opts: EvmOpts, ) -> Result { diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index f463a437e3f8..e8812dce11a1 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -4,7 +4,7 @@ use alloy_primitives::{Address, B256, U256}; use foundry_cli::utils as forge_utils; use foundry_compilers::{ artifacts::{BytecodeHash, OptimizerDetails, RevertStrings, YulDetails}, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, + Solc, }; use foundry_config::{ cache::{CachedChains, CachedEndpoints, StorageCachingConfig}, @@ -43,6 +43,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { gas_reports: vec!["Contract".to_string()], gas_reports_ignore: vec![], solc: Some(SolcReq::Local(PathBuf::from("custom-solc"))), + vyper: Some(PathBuf::from("custom-vyper")), auto_detect_solc: false, auto_detect_remappings: true, offline: true, @@ -365,8 +366,7 @@ contract Foo {} assert!(cmd.stderr_lossy().contains("`solc` this/solc/does/not/exist does not exist")); // `OTHER_SOLC_VERSION` was installed in previous step, so we can use the path to this directly - let local_solc = - SolcVersionManager::default().get_or_install(&OTHER_SOLC_VERSION.parse().unwrap()).unwrap(); + let local_solc = Solc::find_or_install(&OTHER_SOLC_VERSION.parse().unwrap()).unwrap(); cmd.forge_fuse().args(["build", "--force", "--use"]).arg(local_solc.solc).root_arg(); let stdout = cmd.stdout_lossy(); assert!(stdout.contains("Compiler run successful")); diff --git a/crates/forge/tests/cli/test_cmd.rs b/crates/forge/tests/cli/test_cmd.rs index 22e928a431b0..c9f76a3aa611 100644 --- a/crates/forge/tests/cli/test_cmd.rs +++ b/crates/forge/tests/cli/test_cmd.rs @@ -266,6 +266,7 @@ contract ContractTest is DSTest { // tests that libraries are handled correctly in multiforking mode forgetest_init!(can_use_libs_in_multi_fork, |prj, cmd| { prj.wipe_contracts(); + prj.add_source( "Contract.sol", r" diff --git a/crates/script/src/verify.rs b/crates/script/src/verify.rs index 6437e9a5d929..11dab9575a99 100644 --- a/crates/script/src/verify.rs +++ b/crates/script/src/verify.rs @@ -112,10 +112,12 @@ impl VerifyBundle { if data.split_at(create2_offset).1.starts_with(bytecode) { let constructor_args = data.split_at(create2_offset + bytecode.len()).1.to_vec(); + if artifact.source.extension().map_or(false, |e| e.to_str() == Some("vy")) { + warn!("Skipping verification of Vyper contract: {}", artifact.name); + } + let contract = ContractInfo { - path: Some( - artifact.source.to_str().expect("There should be an artifact.").to_string(), - ), + path: Some(artifact.source.to_string_lossy().to_string()), name: artifact.name.clone(), }; diff --git a/crates/test-utils/src/util.rs b/crates/test-utils/src/util.rs index 37b927e19877..0ada068fde28 100644 --- a/crates/test-utils/src/util.rs +++ b/crates/test-utils/src/util.rs @@ -3,9 +3,10 @@ use eyre::{Result, WrapErr}; use foundry_compilers::{ artifacts::Settings, cache::CompilerCache, + compilers::multi::MultiCompiler, error::Result as SolcResult, project_util::{copy_dir, TempProject}, - ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, Solc, + ArtifactOutput, ConfigurableArtifacts, PathStyle, ProjectPathsConfig, }; use foundry_config::Config; use once_cell::sync::Lazy; @@ -409,7 +410,7 @@ pub struct TestProject { /// The directory in which this test executable is running. exe_root: PathBuf, /// The project in which the test should run. - inner: Arc>, + inner: Arc>, } impl TestProject { diff --git a/crates/verify/src/bytecode.rs b/crates/verify/src/bytecode.rs index 829efd42624c..8a2a49f9c4ae 100644 --- a/crates/verify/src/bytecode.rs +++ b/crates/verify/src/bytecode.rs @@ -15,7 +15,7 @@ use foundry_common::{ use foundry_compilers::{ artifacts::{BytecodeHash, BytecodeObject, CompactContractBytecode}, info::ContractInfo, - Artifact, EvmVersion, SolcSparseFileFilter, + Artifact, EvmVersion, }; use foundry_config::{figment, impl_figment_convert, Chain, Config}; use foundry_evm::{ @@ -378,10 +378,7 @@ impl VerifyBytecodeArgs { if let Some(skip) = &self.skip { if !skip.is_empty() { - let filter = SolcSparseFileFilter::new(SkipBuildFilters::new( - skip.to_owned(), - project.root().to_path_buf(), - )?); + let filter = SkipBuildFilters::new(skip.to_owned(), project.root().to_path_buf())?; compiler = compiler.filter(Box::new(filter)); } } diff --git a/crates/verify/src/etherscan/flatten.rs b/crates/verify/src/etherscan/flatten.rs index cebdfbd22e7a..1542f3a0e3b0 100644 --- a/crates/verify/src/etherscan/flatten.rs +++ b/crates/verify/src/etherscan/flatten.rs @@ -4,8 +4,11 @@ use eyre::{Context, Result}; use foundry_block_explorers::verify::CodeFormat; use foundry_compilers::{ artifacts::{BytecodeHash, Source}, - compilers::{solc::SolcVersionManager, Compiler, CompilerVersionManager}, - AggregatedCompilerOutput, SolcInput, + compilers::{ + solc::{SolcCompiler, SolcLanguage, SolcVersionedInput}, + Compiler, CompilerInput, + }, + AggregatedCompilerOutput, Solc, }; use semver::{BuildMetadata, Version}; use std::{collections::BTreeMap, path::Path}; @@ -18,7 +21,7 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { args: &VerifyArgs, context: &VerificationContext, ) -> Result<(String, String, CodeFormat)> { - let metadata = context.project.settings.metadata.as_ref(); + let metadata = context.project.settings.solc.metadata.as_ref(); let bch = metadata.and_then(|m| m.bytecode_hash).unwrap_or_default(); eyre::ensure!( @@ -27,8 +30,13 @@ impl EtherscanSourceProvider for EtherscanFlattenedSource { bch, ); - let source = - context.project.flatten(&context.target_path).wrap_err("Failed to flatten contract")?; + let source = context + .project + .paths + .clone() + .with_language::() + .flatten(&context.target_path) + .wrap_err("Failed to flatten contract")?; if !args.force { // solc dry run of flattened code @@ -67,17 +75,17 @@ impl EtherscanFlattenedSource { version: &Version, contract_path: &Path, ) -> Result<()> { - let vm = SolcVersionManager::default(); let version = strip_build_meta(version.clone()); - let solc = vm.get_or_install(&version)?; + let solc = Solc::find_or_install(&version)?; - let input = SolcInput { - language: "Solidity".to_string(), - sources: BTreeMap::from([("contract.sol".into(), Source::new(content))]), - settings: Default::default(), - }; + let input = SolcVersionedInput::build( + BTreeMap::from([("contract.sol".into(), Source::new(content))]), + Default::default(), + SolcLanguage::Solidity, + version.clone(), + ); - let out = Compiler::compile(&solc, &input)?; + let out = SolcCompiler::Specific(solc).compile(&input)?; if out.errors.iter().any(|e| e.is_error()) { let mut o = AggregatedCompilerOutput::default(); o.extend(version, out); diff --git a/crates/verify/src/provider.rs b/crates/verify/src/provider.rs index 210283c3ac84..cc65078d3f7b 100644 --- a/crates/verify/src/provider.rs +++ b/crates/verify/src/provider.rs @@ -8,8 +8,8 @@ use eyre::{OptionExt, Result}; use foundry_common::compile::ProjectCompiler; use foundry_compilers::{ artifacts::{output_selection::OutputSelection, Metadata, Source}, - compilers::{solc::SolcVersionManager, CompilerVersionManager}, - CompilerConfig, Graph, Project, + compilers::{multi::MultiCompilerParsedSource, solc::SolcCompiler, CompilerSettings}, + Graph, Project, Solc, }; use foundry_config::Config; use semver::Version; @@ -35,10 +35,8 @@ impl VerificationContext { let mut project = config.project()?; project.no_artifacts = true; - // Set project's compiler to always use resolved version. - let vm = SolcVersionManager::default(); - let solc = vm.get_or_install(&compiler_version)?; - project.compiler_config = CompilerConfig::Specific(solc); + let solc = Solc::find_or_install(&compiler_version)?; + project.compiler.solc = SolcCompiler::Specific(solc); Ok(Self { config, project, target_name, target_path, compiler_version }) } @@ -46,8 +44,9 @@ impl VerificationContext { /// Compiles target contract requesting only ABI and returns it. pub fn get_target_abi(&self) -> Result { let mut project = self.project.clone(); - project.settings.output_selection = - OutputSelection::common_output_selection(["abi".to_string()]); + project.settings.update_output_selection(|selection| { + *selection = OutputSelection::common_output_selection(["abi".to_string()]) + }); let output = ProjectCompiler::new() .quiet(true) @@ -64,8 +63,9 @@ impl VerificationContext { /// Compiles target file requesting only metadata and returns it. pub fn get_target_metadata(&self) -> Result { let mut project = self.project.clone(); - project.settings.output_selection = - OutputSelection::common_output_selection(["metadata".to_string()]); + project.settings.update_output_selection(|selection| { + *selection = OutputSelection::common_output_selection(["metadata".to_string()]); + }); let output = ProjectCompiler::new() .quiet(true) @@ -83,7 +83,8 @@ impl VerificationContext { pub fn get_target_imports(&self) -> Result> { let mut sources = self.project.paths.read_input_files()?; sources.insert(self.target_path.clone(), Source::read(&self.target_path)?); - let graph = Graph::resolve_sources(&self.project.paths, sources)?; + let graph = + Graph::::resolve_sources(&self.project.paths, sources)?; Ok(graph.imports(&self.target_path).into_iter().cloned().collect()) }