From 6d5b833d56db1ca8c4b4123f27368b1af35c8db5 Mon Sep 17 00:00:00 2001 From: tringuyenskymavis Date: Wed, 31 Jul 2024 16:51:13 +0700 Subject: [PATCH 1/4] feat(foundry-ignore-compile): read from ignore file and filter out all ignore files --- crates/config/src/lib.rs | 96 +++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d9c7625810274..0b2065f2afbc5 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -16,7 +16,7 @@ use figment::{ value::{Dict, Map, Value}, Error, Figment, Metadata, Profile, Provider, }; -use filter::GlobMatcher; +use filter::{GlobMatcher, SkipBuildFilter}; use foundry_compilers::{ artifacts::{ output_selection::{ContractOutputSelection, OutputSelection}, @@ -44,7 +44,8 @@ use serde::{Deserialize, Serialize, Serializer}; use std::{ borrow::Cow, collections::HashMap, - fs, + fs::{self, File}, + io::{self, BufRead}, path::{Path, PathBuf}, str::FromStr, }; @@ -834,6 +835,21 @@ impl Config { .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts); + let foundry_ignored_paths: Vec = self.get_ignore_paths()?; + + if foundry_ignored_paths.is_empty() { + println!("No ignored paths found"); + } + + let ignore_pattern: Vec = foundry_ignored_paths + .iter() + .filter_map(|path| GlobMatcher::from_str(path).ok()) + .collect(); + + let filter: SkipBuildFilters = + SkipBuildFilters::new(ignore_pattern.clone(), self.root.0.clone()); + builder = builder.sparse_output(filter); + if !self.skip.is_empty() { let filter = SkipBuildFilters::new(self.skip.clone(), self.root.0.clone()); builder = builder.sparse_output(filter); @@ -886,7 +902,7 @@ impl Config { if self.offline { return Err(SolcError::msg(format!( "can't install missing solc {version} in offline mode" - ))) + ))); } Solc::blocking_install(version)? } @@ -896,12 +912,12 @@ impl Config { return Err(SolcError::msg(format!( "`solc` {} does not exist", solc.display() - ))) + ))); } Solc::new(solc)? } }; - return Ok(Some(solc)) + return Ok(Some(solc)); } Ok(None) @@ -919,16 +935,16 @@ impl Config { /// `auto_detect_solc` pub fn is_auto_detect(&self) -> bool { if self.solc.is_some() { - return false + return false; } self.auto_detect_solc } /// Whether caching should be enabled for the given chain id pub fn enable_caching(&self, endpoint: &str, chain_id: impl Into) -> bool { - !self.no_storage_caching && - self.rpc_storage_caching.enable_for_chain_id(chain_id.into()) && - self.rpc_storage_caching.enable_for_endpoint(endpoint) + !self.no_storage_caching + && self.rpc_storage_caching.enable_for_chain_id(chain_id.into()) + && self.rpc_storage_caching.enable_for_endpoint(endpoint) } /// Returns the `ProjectPathsConfig` sub set of the config. @@ -978,7 +994,7 @@ impl Config { pub fn vyper_compiler(&self) -> Result, SolcError> { // Only instantiate Vyper if there are any Vyper files in the project. if self.project_paths::().input_files_iter().next().is_none() { - return Ok(None) + return Ok(None); } let vyper = if let Some(path) = &self.vyper.path { Some(Vyper::new(path)?) @@ -1063,6 +1079,24 @@ impl Config { } } + pub fn get_ignore_paths(&self) -> Result, SolcError> { + let path = Path::new("foundry.ignore"); + let file = File::open(&path).unwrap(); + + let reader = io::BufReader::new(file); + + let mut entries: Vec = Vec::new(); + for line in reader.lines() { + let line = line.map_err(|e| SolcError::msg(format!("Failed to read line: {}", e)))?; + let trimmed = line.trim(); + if !trimmed.is_empty() && !trimmed.starts_with('#') { + entries.push(trimmed.to_string()); + } + } + + Ok(entries) + } + /// Resolves the given alias to a matching rpc url /// /// Returns: @@ -1160,7 +1194,7 @@ impl Config { ) -> Result, EtherscanConfigError> { if let Some(maybe_alias) = self.etherscan_api_key.as_ref().or(self.eth_rpc_url.as_ref()) { if self.etherscan.contains_key(maybe_alias) { - return self.etherscan.clone().resolved().remove(maybe_alias).transpose() + return self.etherscan.clone().resolved().remove(maybe_alias).transpose(); } } @@ -1174,7 +1208,7 @@ impl Config { // we update the key, because if an etherscan_api_key is set, it should take // precedence over the entry, since this is usually set via env var or CLI args. config.key.clone_from(key); - return Ok(Some(config)) + return Ok(Some(config)); } (Ok(config), None) => return Ok(Some(config)), (Err(err), None) => return Err(err), @@ -1187,7 +1221,7 @@ impl Config { // etherscan fallback via API key if let Some(key) = self.etherscan_api_key.as_ref() { let chain = chain.or(self.chain).unwrap_or_default(); - return Ok(ResolvedEtherscanConfig::create(key, chain)) + return Ok(ResolvedEtherscanConfig::create(key, chain)); } Ok(None) @@ -1471,7 +1505,7 @@ impl Config { { let file_path = self.get_config_path(); if !file_path.exists() { - return Ok(()) + return Ok(()); } let contents = fs::read_to_string(&file_path)?; let mut doc = contents.parse::()?; @@ -1633,14 +1667,14 @@ impl Config { return match path.is_file() { true => Some(path.to_path_buf()), false => None, - } + }; } let cwd = std::env::current_dir().ok()?; let mut cwd = cwd.as_path(); loop { let file_path = cwd.join(path); if file_path.is_file() { - return Some(file_path) + return Some(file_path); } cwd = cwd.parent()?; } @@ -1714,7 +1748,7 @@ impl Config { if let Some(cache_dir) = Self::foundry_rpc_cache_dir() { let mut cache = Cache { chains: vec![] }; if !cache_dir.exists() { - return Ok(cache) + return Ok(cache); } if let Ok(entries) = cache_dir.as_path().read_dir() { for entry in entries.flatten().filter(|x| x.path().is_dir()) { @@ -1758,19 +1792,19 @@ impl Config { fn get_cached_blocks(chain_path: &Path) -> eyre::Result> { let mut blocks = vec![]; if !chain_path.exists() { - return Ok(blocks) + return Ok(blocks); } for block in chain_path.read_dir()?.flatten() { let file_type = block.file_type()?; let file_name = block.file_name(); let filepath = if file_type.is_dir() { block.path().join("storage.json") - } else if file_type.is_file() && - file_name.to_string_lossy().chars().all(char::is_numeric) + } else if file_type.is_file() + && file_name.to_string_lossy().chars().all(char::is_numeric) { block.path() } else { - continue + continue; }; blocks.push((file_name.to_string_lossy().into_owned(), fs::metadata(filepath)?.len())); } @@ -1780,7 +1814,7 @@ impl Config { /// The path provided to this function should point to the etherscan cache for a chain. fn get_cached_block_explorer_data(chain_path: &Path) -> eyre::Result { if !chain_path.exists() { - return Ok(0) + return Ok(0); } fn dir_size_recursive(mut dir: fs::ReadDir) -> eyre::Result { @@ -1956,7 +1990,7 @@ pub(crate) mod from_opt_glob { { let s: Option = Option::deserialize(deserializer)?; if let Some(s) = s { - return Ok(Some(globset::Glob::new(&s).map_err(serde::de::Error::custom)?)) + return Ok(Some(globset::Glob::new(&s).map_err(serde::de::Error::custom)?)); } Ok(None) } @@ -2238,7 +2272,7 @@ impl TomlFileProvider { if let Some(file) = self.env_val() { let path = Path::new(&file); if !path.exists() { - return true + return true; } } false @@ -2258,7 +2292,7 @@ impl TomlFileProvider { "Config file `{}` set in env var `{}` does not exist", file, self.env_var.unwrap() - ))) + ))); } Toml::file(file) } else { @@ -2302,7 +2336,7 @@ impl Provider for ForcedSnakeCaseData

{ if Config::STANDALONE_SECTIONS.contains(&profile.as_ref()) { // don't force snake case for keys in standalone sections map.insert(profile, dict); - continue + continue; } map.insert(profile, dict.into_iter().map(|(k, v)| (k.to_snake_case(), v)).collect()); } @@ -2442,7 +2476,7 @@ impl Provider for DappEnvCompatProvider { if val > 1 { return Err( format!("Invalid $DAPP_BUILD_OPTIMIZE value `{val}`, expected 0 or 1").into() - ) + ); } dict.insert("optimizer".to_string(), (val == 1).into()); } @@ -2508,7 +2542,7 @@ impl Provider for RenameProfileProvider

{ fn data(&self) -> Result, Error> { let mut data = self.provider.data()?; if let Some(data) = data.remove(&self.from) { - return Ok(Map::from([(self.to.clone(), data)])) + return Ok(Map::from([(self.to.clone(), data)])); } Ok(Default::default()) } @@ -2554,7 +2588,7 @@ impl Provider for UnwrapProfileProvider

{ for (profile_str, profile_val) in profiles { let profile = Profile::new(&profile_str); if profile != self.profile { - continue + continue; } match profile_val { Value::Dict(_, dict) => return Ok(profile.collect(dict)), @@ -2565,7 +2599,7 @@ impl Provider for UnwrapProfileProvider

{ )); err.metadata = Some(self.provider.metadata()); err.profile = Some(self.profile.clone()); - return Err(err) + return Err(err); } } } @@ -2677,7 +2711,7 @@ impl Provider for OptionalStrictProfileProvider

{ // provider and can't map the metadata to the error. Therefor we return the root error // if this error originated in the provider's data. if let Err(root_err) = self.provider.data() { - return root_err + return root_err; } err }) From 92a42c58ac38964228a20202cc73b69d038e1c92 Mon Sep 17 00:00:00 2001 From: tringuyenskymavis Date: Thu, 1 Aug 2024 10:16:03 +0700 Subject: [PATCH 2/4] fix: crash when foundry ignore file does not exist --- crates/config/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 0b2065f2afbc5..d8a692833f5a6 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -846,9 +846,9 @@ impl Config { .filter_map(|path| GlobMatcher::from_str(path).ok()) .collect(); - let filter: SkipBuildFilters = + let ignore_filter: SkipBuildFilters = SkipBuildFilters::new(ignore_pattern.clone(), self.root.0.clone()); - builder = builder.sparse_output(filter); + builder = builder.sparse_output(ignore_filter); if !self.skip.is_empty() { let filter = SkipBuildFilters::new(self.skip.clone(), self.root.0.clone()); @@ -1081,10 +1081,11 @@ impl Config { pub fn get_ignore_paths(&self) -> Result, SolcError> { let path = Path::new("foundry.ignore"); + if !path.exists() { + return Ok(Vec::new()); + } let file = File::open(&path).unwrap(); - let reader = io::BufReader::new(file); - let mut entries: Vec = Vec::new(); for line in reader.lines() { let line = line.map_err(|e| SolcError::msg(format!("Failed to read line: {}", e)))?; From 7d046d73d18d32d613155ea03440a37839d118c7 Mon Sep 17 00:00:00 2001 From: tringuyenskymavis Date: Wed, 7 Aug 2024 14:26:24 +0700 Subject: [PATCH 3/4] chore:"split get_ignored_paths function and fix warning" --- Cargo.lock | 1 + crates/config/Cargo.toml | 1 + crates/config/src/lib.rs | 57 ++++++++++++++++++++++------------------ 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a37ebd14bd4d..a43c86117820d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3849,6 +3849,7 @@ dependencies = [ "toml_edit 0.22.17", "tracing", "walkdir", + "yansi", ] [[package]] diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index c29a5fd44c01b..c8068cd93e8c0 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -43,6 +43,7 @@ toml = { version = "0.8", features = ["preserve_order"] } toml_edit = "0.22.4" tracing.workspace = true walkdir.workspace = true +yansi.workspace = true [target.'cfg(target_os = "windows")'.dependencies] path-slash = "0.2.1" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index d8a692833f5a6..fe130e139d090 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -16,7 +16,7 @@ use figment::{ value::{Dict, Map, Value}, Error, Figment, Metadata, Profile, Provider, }; -use filter::{GlobMatcher, SkipBuildFilter}; +use filter::GlobMatcher; use foundry_compilers::{ artifacts::{ output_selection::{ContractOutputSelection, OutputSelection}, @@ -49,6 +49,7 @@ use std::{ path::{Path, PathBuf}, str::FromStr, }; +use yansi::Paint; mod macros; @@ -835,19 +836,7 @@ impl Config { .set_build_info(!no_artifacts && self.build_info) .set_no_artifacts(no_artifacts); - let foundry_ignored_paths: Vec = self.get_ignore_paths()?; - - if foundry_ignored_paths.is_empty() { - println!("No ignored paths found"); - } - - let ignore_pattern: Vec = foundry_ignored_paths - .iter() - .filter_map(|path| GlobMatcher::from_str(path).ok()) - .collect(); - - let ignore_filter: SkipBuildFilters = - SkipBuildFilters::new(ignore_pattern.clone(), self.root.0.clone()); + let ignore_filter: SkipBuildFilters = self.get_ignore_filter()?; builder = builder.sparse_output(ignore_filter); if !self.skip.is_empty() { @@ -1079,23 +1068,43 @@ impl Config { } } - pub fn get_ignore_paths(&self) -> Result, SolcError> { - let path = Path::new("foundry.ignore"); - if !path.exists() { + pub fn get_ignore_filter(&self) -> Result { + let ignored_paths: Vec = self.get_ignored_paths()?; + + let ignore_pattern: Vec = ignored_paths + .iter() + .filter_map(|path| GlobMatcher::from_str(path).ok()) + .collect(); + + let ignore_filter: SkipBuildFilters = SkipBuildFilters::new(ignore_pattern.clone(), self.root.0.clone()); + Ok(ignore_filter) + } + + pub fn get_ignored_paths(&self) -> Result, SolcError> { + let foundryignore_file_path = Path::new(".foundryignore"); + + if !foundryignore_file_path.exists() { + eprintln!("{}{}", "Warning: ".yellow().bold(), "Can not find .foundryignore".bold()); return Ok(Vec::new()); } - let file = File::open(&path).unwrap(); + + let file = File::open(&foundryignore_file_path).unwrap(); let reader = io::BufReader::new(file); - let mut entries: Vec = Vec::new(); + let mut ignored_paths: Vec = Vec::new(); + for line in reader.lines() { let line = line.map_err(|e| SolcError::msg(format!("Failed to read line: {}", e)))?; let trimmed = line.trim(); if !trimmed.is_empty() && !trimmed.starts_with('#') { - entries.push(trimmed.to_string()); + ignored_paths.push(trimmed.to_string()); } } - Ok(entries) + if ignored_paths.is_empty() { + eprintln!("{}{}", "Warning: ".yellow().bold(), "No skipped paths found in .foundryignore".bold()); + } + + Ok(ignored_paths) } /// Resolves the given alias to a matching rpc url @@ -1865,12 +1874,10 @@ impl Config { STANDALONE_FALLBACK_SECTIONS.iter().find(|(key, _)| standalone_key == key) { figment = figment.merge( - provider - .fallback(standalone_key, fallback) - .wrap(profile.clone(), standalone_key), + ProviderExt::wrap(&provider.fallback(standalone_key, fallback), profile.clone(), standalone_key) ); } else { - figment = figment.merge(provider.wrap(profile.clone(), standalone_key)); + figment = figment.merge(ProviderExt::wrap(&provider, profile.clone(), standalone_key)); } } // merge the profile From 9d1ead902ebdc4d1b64a4abb195a2d1610e49e0b Mon Sep 17 00:00:00 2001 From: tringuyenskymavis Date: Wed, 7 Aug 2024 15:39:56 +0700 Subject: [PATCH 4/4] chore: fix format --- crates/config/src/lib.rs | 42 +++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index fe130e139d090..7f08ab31e0ffe 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -931,9 +931,9 @@ impl Config { /// Whether caching should be enabled for the given chain id pub fn enable_caching(&self, endpoint: &str, chain_id: impl Into) -> bool { - !self.no_storage_caching - && self.rpc_storage_caching.enable_for_chain_id(chain_id.into()) - && self.rpc_storage_caching.enable_for_endpoint(endpoint) + !self.no_storage_caching && + self.rpc_storage_caching.enable_for_chain_id(chain_id.into()) && + self.rpc_storage_caching.enable_for_endpoint(endpoint) } /// Returns the `ProjectPathsConfig` sub set of the config. @@ -1071,12 +1071,11 @@ impl Config { pub fn get_ignore_filter(&self) -> Result { let ignored_paths: Vec = self.get_ignored_paths()?; - let ignore_pattern: Vec = ignored_paths - .iter() - .filter_map(|path| GlobMatcher::from_str(path).ok()) - .collect(); + let ignore_pattern: Vec = + ignored_paths.iter().filter_map(|path| GlobMatcher::from_str(path).ok()).collect(); - let ignore_filter: SkipBuildFilters = SkipBuildFilters::new(ignore_pattern.clone(), self.root.0.clone()); + let ignore_filter: SkipBuildFilters = + SkipBuildFilters::new(ignore_pattern, self.root.0.clone()); Ok(ignore_filter) } @@ -1085,15 +1084,15 @@ impl Config { if !foundryignore_file_path.exists() { eprintln!("{}{}", "Warning: ".yellow().bold(), "Can not find .foundryignore".bold()); - return Ok(Vec::new()); + return Ok(Vec::new()) } - let file = File::open(&foundryignore_file_path).unwrap(); + let file = File::open(foundryignore_file_path).unwrap(); let reader = io::BufReader::new(file); let mut ignored_paths: Vec = Vec::new(); for line in reader.lines() { - let line = line.map_err(|e| SolcError::msg(format!("Failed to read line: {}", e)))?; + let line = line.map_err(|e| SolcError::msg(format!("Failed to read line: {e}")))?; let trimmed = line.trim(); if !trimmed.is_empty() && !trimmed.starts_with('#') { ignored_paths.push(trimmed.to_string()); @@ -1101,7 +1100,11 @@ impl Config { } if ignored_paths.is_empty() { - eprintln!("{}{}", "Warning: ".yellow().bold(), "No skipped paths found in .foundryignore".bold()); + eprintln!( + "{}{}", + "Warning: ".yellow().bold(), + "No skipped paths found in .foundryignore".bold() + ); } Ok(ignored_paths) @@ -1809,8 +1812,8 @@ impl Config { let file_name = block.file_name(); let filepath = if file_type.is_dir() { block.path().join("storage.json") - } else if file_type.is_file() - && file_name.to_string_lossy().chars().all(char::is_numeric) + } else if file_type.is_file() && + file_name.to_string_lossy().chars().all(char::is_numeric) { block.path() } else { @@ -1873,11 +1876,14 @@ impl Config { if let Some((_, fallback)) = STANDALONE_FALLBACK_SECTIONS.iter().find(|(key, _)| standalone_key == key) { - figment = figment.merge( - ProviderExt::wrap(&provider.fallback(standalone_key, fallback), profile.clone(), standalone_key) - ); + figment = figment.merge(ProviderExt::wrap( + &provider.fallback(standalone_key, fallback), + profile.clone(), + standalone_key, + )); } else { - figment = figment.merge(ProviderExt::wrap(&provider, profile.clone(), standalone_key)); + figment = + figment.merge(ProviderExt::wrap(&provider, profile.clone(), standalone_key)); } } // merge the profile