From 3ef8b62ecc2e9033b38a54dd6217f44c40225142 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 16 Oct 2024 18:06:18 +0300 Subject: [PATCH 1/6] feat(forge): add max supported EVM version in compiler -v --- crates/forge/bin/cmd/compiler.rs | 20 ++++++++++++-------- crates/forge/tests/cli/compiler.rs | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/crates/forge/bin/cmd/compiler.rs b/crates/forge/bin/cmd/compiler.rs index badc053c6c4d..76cb061b13ab 100644 --- a/crates/forge/bin/cmd/compiler.rs +++ b/crates/forge/bin/cmd/compiler.rs @@ -1,6 +1,6 @@ use clap::{ArgAction, Parser, Subcommand, ValueHint}; use eyre::Result; -use foundry_compilers::Graph; +use foundry_compilers::{artifacts::EvmVersion, Graph}; use foundry_config::Config; use semver::Version; use std::{collections::BTreeMap, path::PathBuf}; @@ -67,10 +67,10 @@ impl ResolveArgs { &project.compiler, )?; - let mut output: BTreeMap)>> = BTreeMap::new(); + let mut output: BTreeMap)>> = BTreeMap::new(); for (language, sources) in sources { - let mut versions_with_paths: Vec<(Version, Vec)> = sources + let mut versions_with_paths: Vec<(Version, EvmVersion, Vec)> = sources .iter() .map(|(version, sources)| { let paths: Vec = sources @@ -94,13 +94,17 @@ impl ResolveArgs { }) .collect(); - (version.clone(), paths) + ( + version.clone(), + EvmVersion::default().normalize_version_solc(version).unwrap_or_default(), + paths, + ) }) - .filter(|(_, paths)| !paths.is_empty()) + .filter(|(_, _, paths)| !paths.is_empty()) .collect(); // Sort by SemVer version. - versions_with_paths.sort_by(|(v1, _), (v2, _)| Version::cmp(v1, v2)); + versions_with_paths.sort_by(|(v1, _, _), (v2, _, _)| Version::cmp(v1, v2)); // Skip language if no paths are found after filtering. if !versions_with_paths.is_empty() { @@ -120,9 +124,9 @@ impl ResolveArgs { println!("{language}:\n"); } - for (version, paths) in versions { + for (version, evm_version, paths) in versions { if verbosity >= 1 { - println!("{version}:"); + println!("{version} [Max supported EVM version: {evm_version}]:"); for (idx, path) in paths.iter().enumerate() { if idx == paths.len() - 1 { println!("└── {path}\n"); diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index 665356632320..d754ddee8568 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -31,7 +31,7 @@ contract ContractD {} "#; const VYPER_INTERFACE: &str = r#" -# pragma version 0.4.0 +# pragma version >=0.4.0 @external @view @@ -94,7 +94,7 @@ forgetest!(can_list_resolved_compiler_versions_verbose, |prj, cmd| { cmd.args(["compiler", "resolve", "-v"]).assert_success().stdout_eq(str![[r#" Solidity: -0.8.27: +0.8.27 [Max supported EVM version: [..]]: ├── src/ContractC.sol └── src/ContractD.sol @@ -108,7 +108,7 @@ forgetest!(can_list_resolved_compiler_versions_json, |prj, cmd| { cmd.args(["compiler", "resolve", "--json"]).assert_success().stdout_eq( str![[r#" -{"Solidity":[["0.8.27",["src/ContractC.sol","src/ContractD.sol"]]]}"#]] +{"Solidity":[["0.8.27","[..]",["src/ContractC.sol","src/ContractD.sol"]]]}"#]] .is_json(), ); }); @@ -146,7 +146,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped, |prj, cmd| { r#" Vyper: -0.4.0: +0.4.0 [Max supported EVM version: [..]]: ├── src/Counter.vy └── src/ICounter.vyi @@ -166,7 +166,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped_json, |prj, cmd| cmd.args(["compiler", "resolve", "--skip", "Contract(A|B|C)", "--json"]) .assert_success() .stdout_eq(str![[r#" -{"Solidity":[["0.8.27",["src/ContractD.sol"]]],"Vyper":[["0.4.0",["src/Counter.vy","src/ICounter.vyi"]]]} +{"Solidity":[["0.8.27","[..]",["src/ContractD.sol"]]],"Vyper":[["0.4.0","[..]",["src/Counter.vy","src/ICounter.vyi"]]]} "#]].is_json()); }); @@ -181,19 +181,19 @@ forgetest!(can_list_resolved_multiple_compiler_versions_verbose, |prj, cmd| { cmd.args(["compiler", "resolve", "-v"]).assert_success().stdout_eq(str![[r#" Solidity: -0.8.4: +0.8.4 [Max supported EVM version: istanbul]: └── src/ContractA.sol -0.8.11: +0.8.11 [Max supported EVM version: london]: └── src/ContractB.sol -0.8.27: +0.8.27 [Max supported EVM version: [..]]: ├── src/ContractC.sol └── src/ContractD.sol Vyper: -0.4.0: +0.4.0 [Max supported EVM version: [..]]: ├── src/Counter.vy └── src/ICounter.vyi @@ -211,7 +211,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_json, |prj, cmd| { cmd.args(["compiler", "resolve", "--json"]).assert_success().stdout_eq( str![[r#" -{"Solidity":[["0.8.4",["src/ContractA.sol"]],["0.8.11",["src/ContractB.sol"]],["0.8.27",["src/ContractC.sol","src/ContractD.sol"]]],"Vyper":[["0.4.0",["src/Counter.vy","src/ICounter.vyi"]]]} +{"Solidity":[["0.8.4","Istanbul",["src/ContractA.sol"]],["0.8.11","London",["src/ContractB.sol"]],["0.8.27","[..]",["src/ContractC.sol","src/ContractD.sol"]]],"Vyper":[["0.4.0","Cancun",["src/Counter.vy","src/ICounter.vyi"]]]} "#]] .is_json(), ); From efcdf15cc191a9925fbfe340ec37dc615ef269b7 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 16 Oct 2024 19:30:11 +0300 Subject: [PATCH 2/6] shorter message, displayed on -vv verbosity --- crates/forge/bin/cmd/compiler.rs | 15 ++++++++++----- crates/forge/tests/cli/compiler.rs | 14 +++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/crates/forge/bin/cmd/compiler.rs b/crates/forge/bin/cmd/compiler.rs index 76cb061b13ab..0c294adbfddb 100644 --- a/crates/forge/bin/cmd/compiler.rs +++ b/crates/forge/bin/cmd/compiler.rs @@ -125,8 +125,15 @@ impl ResolveArgs { } for (version, evm_version, paths) in versions { - if verbosity >= 1 { - println!("{version} [Max supported EVM version: {evm_version}]:"); + if verbosity == 0 { + println!("- {version}"); + } else { + if verbosity == 1 { + println!("{version}:"); + } else { + println!("{version} (<= {evm_version}):") + } + for (idx, path) in paths.iter().enumerate() { if idx == paths.len() - 1 { println!("└── {path}\n"); @@ -134,12 +141,10 @@ impl ResolveArgs { println!("├── {path}"); } } - } else { - println!("- {version}"); } } - if verbosity < 1 { + if verbosity == 0 { println!(); } } diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index d754ddee8568..f8b589bc20f0 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -94,7 +94,7 @@ forgetest!(can_list_resolved_compiler_versions_verbose, |prj, cmd| { cmd.args(["compiler", "resolve", "-v"]).assert_success().stdout_eq(str![[r#" Solidity: -0.8.27 [Max supported EVM version: [..]]: +0.8.27: ├── src/ContractC.sol └── src/ContractD.sol @@ -146,7 +146,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped, |prj, cmd| { r#" Vyper: -0.4.0 [Max supported EVM version: [..]]: +0.4.0: ├── src/Counter.vy └── src/ICounter.vyi @@ -178,22 +178,22 @@ forgetest!(can_list_resolved_multiple_compiler_versions_verbose, |prj, cmd| { prj.add_raw_source("ICounter.vyi", VYPER_INTERFACE).unwrap(); prj.add_raw_source("Counter.vy", VYPER_CONTRACT).unwrap(); - cmd.args(["compiler", "resolve", "-v"]).assert_success().stdout_eq(str![[r#" + cmd.args(["compiler", "resolve", "-vv"]).assert_success().stdout_eq(str![[r#" Solidity: -0.8.4 [Max supported EVM version: istanbul]: +0.8.4 (<= istanbul): └── src/ContractA.sol -0.8.11 [Max supported EVM version: london]: +0.8.11 (<= london): └── src/ContractB.sol -0.8.27 [Max supported EVM version: [..]]: +0.8.27 (<= cancun): ├── src/ContractC.sol └── src/ContractD.sol Vyper: -0.4.0 [Max supported EVM version: [..]]: +0.4.0 (<= cancun): ├── src/Counter.vy └── src/ICounter.vyi From 81cf6ca48608101f36696c6256bbfac286cc04bf Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 16 Oct 2024 19:47:17 +0300 Subject: [PATCH 3/6] match on verbosity --- crates/forge/bin/cmd/compiler.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/crates/forge/bin/cmd/compiler.rs b/crates/forge/bin/cmd/compiler.rs index 0c294adbfddb..24d5a3f2544f 100644 --- a/crates/forge/bin/cmd/compiler.rs +++ b/crates/forge/bin/cmd/compiler.rs @@ -118,22 +118,19 @@ impl ResolveArgs { } for (language, versions) in &output { - if verbosity < 1 { - println!("{language}:"); - } else { - println!("{language}:\n"); + match verbosity { + 0 => println!("{language}:"), + _ => println!("{language}:\n"), } for (version, evm_version, paths) in versions { - if verbosity == 0 { - println!("- {version}"); - } else { - if verbosity == 1 { - println!("{version}:"); - } else { - println!("{version} (<= {evm_version}):") - } + match verbosity { + 0 => println!("- {version}"), + 1 => println!("{version}:"), + _ => println!("{version} (<= {evm_version}):"), + } + if verbosity > 0 { for (idx, path) in paths.iter().enumerate() { if idx == paths.len() - 1 { println!("└── {path}\n"); From 588bc53285efb48f2398f696ad1845b2d94460c0 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 16 Oct 2024 22:11:28 +0300 Subject: [PATCH 4/6] Respect verbosity in json, nicer json output --- crates/forge/bin/cmd/compiler.rs | 52 ++++++++++++++----- crates/forge/tests/cli/compiler.rs | 82 +++++++++++++++++++++++++++--- 2 files changed, 114 insertions(+), 20 deletions(-) diff --git a/crates/forge/bin/cmd/compiler.rs b/crates/forge/bin/cmd/compiler.rs index 24d5a3f2544f..50846afbb4c0 100644 --- a/crates/forge/bin/cmd/compiler.rs +++ b/crates/forge/bin/cmd/compiler.rs @@ -3,6 +3,7 @@ use eyre::Result; use foundry_compilers::{artifacts::EvmVersion, Graph}; use foundry_config::Config; use semver::Version; +use serde::Serialize; use std::{collections::BTreeMap, path::PathBuf}; /// CLI arguments for `forge compiler`. @@ -27,6 +28,18 @@ pub enum CompilerSubcommands { Resolve(ResolveArgs), } +/// Resolved compiler within the project. +#[derive(Serialize)] +struct ResolvedCompiler { + /// Compiler version. + version: Version, + #[serde(skip_serializing_if = "Option::is_none")] + /// Max supported EVM version of compiler. + evm_version: Option, + /// Source paths. + paths: Vec, +} + /// CLI arguments for `forge compiler resolve`. #[derive(Debug, Parser)] pub struct ResolveArgs { @@ -67,10 +80,10 @@ impl ResolveArgs { &project.compiler, )?; - let mut output: BTreeMap)>> = BTreeMap::new(); + let mut output: BTreeMap> = BTreeMap::new(); for (language, sources) in sources { - let mut versions_with_paths: Vec<(Version, EvmVersion, Vec)> = sources + let mut versions_with_paths: Vec = sources .iter() .map(|(version, sources)| { let paths: Vec = sources @@ -94,17 +107,23 @@ impl ResolveArgs { }) .collect(); - ( - version.clone(), - EvmVersion::default().normalize_version_solc(version).unwrap_or_default(), - paths, - ) + let evm_version = if verbosity > 1 { + Some( + EvmVersion::default() + .normalize_version_solc(version) + .unwrap_or_default(), + ) + } else { + None + }; + + ResolvedCompiler { version: version.clone(), evm_version, paths } }) - .filter(|(_, _, paths)| !paths.is_empty()) + .filter(|version| !version.paths.is_empty()) .collect(); // Sort by SemVer version. - versions_with_paths.sort_by(|(v1, _, _), (v2, _, _)| Version::cmp(v1, v2)); + versions_with_paths.sort_by(|v1, v2| Version::cmp(&v1.version, &v2.version)); // Skip language if no paths are found after filtering. if !versions_with_paths.is_empty() { @@ -117,20 +136,27 @@ impl ResolveArgs { return Ok(()); } - for (language, versions) in &output { + for (language, compilers) in &output { match verbosity { 0 => println!("{language}:"), _ => println!("{language}:\n"), } - for (version, evm_version, paths) in versions { + for resolved_compiler in compilers { + let version = &resolved_compiler.version; match verbosity { 0 => println!("- {version}"), - 1 => println!("{version}:"), - _ => println!("{version} (<= {evm_version}):"), + _ => { + if let Some(evm) = &resolved_compiler.evm_version { + println!("{version} (<= {evm}):") + } else { + println!("{version}:") + } + } } if verbosity > 0 { + let paths = &resolved_compiler.paths; for (idx, path) in paths.iter().enumerate() { if idx == paths.len() - 1 { println!("└── {path}\n"); diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index f8b589bc20f0..e8dc26c8731c 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -108,7 +108,18 @@ forgetest!(can_list_resolved_compiler_versions_json, |prj, cmd| { cmd.args(["compiler", "resolve", "--json"]).assert_success().stdout_eq( str![[r#" -{"Solidity":[["0.8.27","[..]",["src/ContractC.sol","src/ContractD.sol"]]]}"#]] +{ + "Solidity": [ + { + "version": "0.8.27", + "paths": [ + "src/ContractC.sol", + "src/ContractD.sol" + ] + } + ] +} +"#]] .is_json(), ); }); @@ -164,10 +175,32 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped_json, |prj, cmd| prj.add_raw_source("Counter.vy", VYPER_CONTRACT).unwrap(); cmd.args(["compiler", "resolve", "--skip", "Contract(A|B|C)", "--json"]) - .assert_success() - .stdout_eq(str![[r#" -{"Solidity":[["0.8.27","[..]",["src/ContractD.sol"]]],"Vyper":[["0.4.0","[..]",["src/Counter.vy","src/ICounter.vyi"]]]} -"#]].is_json()); + .assert_success() + .stdout_eq( + str![[r#" +{ + "Solidity": [ + { + "version": "0.8.27", + "paths": [ + "src/ContractD.sol" + ] + } + ], + "Vyper": [ + { + "version": "0.4.0", + "paths": [ + "src/Counter.vy", + "src/ICounter.vyi" + ] + } + ] +} + +"#]] + .is_json(), + ); }); forgetest!(can_list_resolved_multiple_compiler_versions_verbose, |prj, cmd| { @@ -209,9 +242,44 @@ forgetest!(can_list_resolved_multiple_compiler_versions_json, |prj, cmd| { prj.add_raw_source("ICounter.vyi", VYPER_INTERFACE).unwrap(); prj.add_raw_source("Counter.vy", VYPER_CONTRACT).unwrap(); - cmd.args(["compiler", "resolve", "--json"]).assert_success().stdout_eq( + cmd.args(["compiler", "resolve", "--json", "-vv"]).assert_success().stdout_eq( str![[r#" -{"Solidity":[["0.8.4","Istanbul",["src/ContractA.sol"]],["0.8.11","London",["src/ContractB.sol"]],["0.8.27","[..]",["src/ContractC.sol","src/ContractD.sol"]]],"Vyper":[["0.4.0","Cancun",["src/Counter.vy","src/ICounter.vyi"]]]} +{ + "Solidity": [ + { + "version": "0.8.4", + "evm_version": "Istanbul", + "paths": [ + "src/ContractA.sol" + ] + }, + { + "version": "0.8.11", + "evm_version": "London", + "paths": [ + "src/ContractB.sol" + ] + }, + { + "version": "0.8.27", + "evm_version": "Cancun", + "paths": [ + "src/ContractC.sol", + "src/ContractD.sol" + ] + } + ], + "Vyper": [ + { + "version": "0.4.0", + "evm_version": "Cancun", + "paths": [ + "src/Counter.vy", + "src/ICounter.vyi" + ] + } + ] +} "#]] .is_json(), ); From 7d93daaebb24a6325bee8bbf6bd5ee5d8662e678 Mon Sep 17 00:00:00 2001 From: grandizzy Date: Wed, 16 Oct 2024 22:27:50 +0300 Subject: [PATCH 5/6] Redact default EVM version in tests --- crates/forge/tests/cli/compiler.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index e8dc26c8731c..e6d7e0e8d85f 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -220,13 +220,13 @@ Solidity: 0.8.11 (<= london): └── src/ContractB.sol -0.8.27 (<= cancun): +0.8.27 (<= [..]): ├── src/ContractC.sol └── src/ContractD.sol Vyper: -0.4.0 (<= cancun): +0.4.0 (<= [..]): ├── src/Counter.vy └── src/ICounter.vyi @@ -262,7 +262,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_json, |prj, cmd| { }, { "version": "0.8.27", - "evm_version": "Cancun", + "evm_version": "[..]", "paths": [ "src/ContractC.sol", "src/ContractD.sol" @@ -272,7 +272,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_json, |prj, cmd| { "Vyper": [ { "version": "0.4.0", - "evm_version": "Cancun", + "evm_version": "[..]", "paths": [ "src/Counter.vy", "src/ICounter.vyi" From 3369995354bf01519c4396367ac52fe4e2f7fb9c Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Wed, 16 Oct 2024 23:20:35 +0200 Subject: [PATCH 6/6] make --json output not output paths in verbosity mode 0, equivalent of non-verbose text mode --- crates/forge/bin/cmd/compiler.rs | 13 +++++++++++-- crates/forge/tests/cli/compiler.rs | 26 +++++++++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/crates/forge/bin/cmd/compiler.rs b/crates/forge/bin/cmd/compiler.rs index 50846afbb4c0..06823c0c60b0 100644 --- a/crates/forge/bin/cmd/compiler.rs +++ b/crates/forge/bin/cmd/compiler.rs @@ -33,10 +33,11 @@ pub enum CompilerSubcommands { struct ResolvedCompiler { /// Compiler version. version: Version, - #[serde(skip_serializing_if = "Option::is_none")] /// Max supported EVM version of compiler. + #[serde(skip_serializing_if = "Option::is_none")] evm_version: Option, /// Source paths. + #[serde(skip_serializing_if = "Vec::is_empty")] paths: Vec, } @@ -56,7 +57,9 @@ pub struct ResolveArgs { /// Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv). /// /// Verbosity levels: - /// - 2: Print source paths. + /// - 0: Print compiler versions. + /// - 1: Print compiler version and source paths. + /// - 2: Print compiler version, source paths and max supported EVM version of the compiler. #[arg(long, short, verbatim_doc_comment, action = ArgAction::Count, help_heading = "Display options")] pub verbosity: u8, @@ -127,6 +130,12 @@ impl ResolveArgs { // Skip language if no paths are found after filtering. if !versions_with_paths.is_empty() { + // Clear paths if verbosity is 0, performed only after filtering to avoid being + // skipped. + if verbosity == 0 { + versions_with_paths.iter_mut().for_each(|version| version.paths.clear()); + } + output.insert(language.to_string(), versions_with_paths); } } diff --git a/crates/forge/tests/cli/compiler.rs b/crates/forge/tests/cli/compiler.rs index e6d7e0e8d85f..b8453b67b944 100644 --- a/crates/forge/tests/cli/compiler.rs +++ b/crates/forge/tests/cli/compiler.rs @@ -87,6 +87,23 @@ Solidity: "#]]); }); +forgetest!(can_list_resolved_compiler_versions_json, |prj, cmd| { + prj.add_source("ContractA", CONTRACT_A).unwrap(); + + cmd.args(["compiler", "resolve", "--json"]).assert_success().stdout_eq( + str![[r#" +{ + "Solidity":[ + { + "version":"0.8.4" + } + ] +} +"#]] + .is_json(), + ); +}); + forgetest!(can_list_resolved_compiler_versions_verbose, |prj, cmd| { prj.add_source("ContractC", CONTRACT_C).unwrap(); prj.add_source("ContractD", CONTRACT_D).unwrap(); @@ -102,11 +119,11 @@ Solidity: "#]]); }); -forgetest!(can_list_resolved_compiler_versions_json, |prj, cmd| { +forgetest!(can_list_resolved_compiler_versions_verbose_json, |prj, cmd| { prj.add_source("ContractC", CONTRACT_C).unwrap(); prj.add_source("ContractD", CONTRACT_D).unwrap(); - cmd.args(["compiler", "resolve", "--json"]).assert_success().stdout_eq( + cmd.args(["compiler", "resolve", "--json", "-v"]).assert_success().stdout_eq( str![[r#" { "Solidity": [ @@ -174,7 +191,7 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped_json, |prj, cmd| prj.add_raw_source("ICounter.vyi", VYPER_INTERFACE).unwrap(); prj.add_raw_source("Counter.vy", VYPER_CONTRACT).unwrap(); - cmd.args(["compiler", "resolve", "--skip", "Contract(A|B|C)", "--json"]) + cmd.args(["compiler", "resolve", "--skip", "Contract(A|B|C)", "--json", "-v"]) .assert_success() .stdout_eq( str![[r#" @@ -197,7 +214,6 @@ forgetest!(can_list_resolved_multiple_compiler_versions_skipped_json, |prj, cmd| } ] } - "#]] .is_json(), ); @@ -234,7 +250,7 @@ Vyper: "#]]); }); -forgetest!(can_list_resolved_multiple_compiler_versions_json, |prj, cmd| { +forgetest!(can_list_resolved_multiple_compiler_versions_verbose_json, |prj, cmd| { prj.add_source("ContractA", CONTRACT_A).unwrap(); prj.add_source("ContractB", CONTRACT_B).unwrap(); prj.add_source("ContractC", CONTRACT_C).unwrap();