Skip to content

Commit a160287

Browse files
jpgonzalezrazerosnacksDaniPopes
authored andcommitted
feat(forge): add compiler subcommand (foundry-rs#7909)
* feat(forge): add solc subcommand and utilities * style: improve formatting in solc.rs file * fix: merge * add json compatible output * add basic tests * add basic tests * clean up * finish tests * add skip flag * add vyper for unit tests * move tests, pin compiler version, use forgetest! * update CI test location for target Python / Vyper * update foundry-compilers crate * Update crates/forge/bin/cmd/compiler.rs Co-authored-by: DaniPopes <[email protected]> * `compiler` command is sync, remove conditions on CI for Vyper / Python installs * is_jsonlines -> is_json --------- Co-authored-by: zerosnacks <[email protected]> Co-authored-by: zerosnacks <[email protected]> Co-authored-by: DaniPopes <[email protected]>
1 parent 6bb42a3 commit a160287

File tree

9 files changed

+386
-18
lines changed

9 files changed

+386
-18
lines changed

.github/workflows/nextest.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,10 @@ jobs:
6767
with:
6868
bun-version: latest
6969
- name: Setup Python
70-
if: contains(matrix.name, 'external')
7170
uses: actions/setup-python@v4
7271
with:
7372
python-version: 3.11
7473
- name: Install Vyper
75-
if: contains(matrix.name, 'external')
7674
run: pip install vyper~=0.4.0
7775

7876
- name: Forge RPC cache

Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ foundry-linking = { path = "crates/linking" }
169169

170170
# solc & compilation utilities
171171
foundry-block-explorers = { version = "0.7.3", default-features = false }
172-
foundry-compilers = { version = "0.11.4", default-features = false }
172+
foundry-compilers = { version = "0.11.5", default-features = false }
173173
foundry-fork-db = "0.4.0"
174174
solang-parser = "=0.3.3"
175175

crates/forge/bin/cmd/compiler.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use clap::{ArgAction, Parser, Subcommand, ValueHint};
2+
use eyre::Result;
3+
use foundry_compilers::Graph;
4+
use foundry_config::Config;
5+
use semver::Version;
6+
use std::{collections::BTreeMap, path::PathBuf};
7+
8+
/// CLI arguments for `forge compiler`.
9+
#[derive(Debug, Parser)]
10+
pub struct CompilerArgs {
11+
#[command(subcommand)]
12+
pub sub: CompilerSubcommands,
13+
}
14+
15+
impl CompilerArgs {
16+
pub fn run(self) -> Result<()> {
17+
match self.sub {
18+
CompilerSubcommands::Resolve(args) => args.run(),
19+
}
20+
}
21+
}
22+
23+
#[derive(Debug, Subcommand)]
24+
pub enum CompilerSubcommands {
25+
/// Retrieves the resolved version(s) of the compiler within the project.
26+
#[command(visible_alias = "r")]
27+
Resolve(ResolveArgs),
28+
}
29+
30+
/// CLI arguments for `forge compiler resolve`.
31+
#[derive(Debug, Parser)]
32+
pub struct ResolveArgs {
33+
/// The root directory
34+
#[arg(long, short, value_hint = ValueHint::DirPath, value_name = "PATH")]
35+
root: Option<PathBuf>,
36+
37+
/// Skip files that match the given regex pattern.
38+
#[arg(long, short, value_name = "REGEX")]
39+
skip: Option<regex::Regex>,
40+
41+
/// Verbosity of the output.
42+
///
43+
/// Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).
44+
///
45+
/// Verbosity levels:
46+
/// - 2: Print source paths.
47+
#[arg(long, short, verbatim_doc_comment, action = ArgAction::Count, help_heading = "Display options")]
48+
pub verbosity: u8,
49+
50+
/// Print as JSON.
51+
#[arg(long, short, help_heading = "Display options")]
52+
json: bool,
53+
}
54+
55+
impl ResolveArgs {
56+
pub fn run(self) -> Result<()> {
57+
let Self { root, skip, verbosity, json } = self;
58+
59+
let root = root.unwrap_or_else(|| PathBuf::from("."));
60+
let config = Config::load_with_root(&root);
61+
let project = config.project()?;
62+
63+
let graph = Graph::resolve(&project.paths)?;
64+
let (sources, _) = graph.into_sources_by_version(
65+
project.offline,
66+
&project.locked_versions,
67+
&project.compiler,
68+
)?;
69+
70+
let mut output: BTreeMap<String, Vec<(Version, Vec<String>)>> = BTreeMap::new();
71+
72+
for (language, sources) in sources {
73+
let mut versions_with_paths: Vec<(Version, Vec<String>)> = sources
74+
.iter()
75+
.map(|(version, sources)| {
76+
let paths: Vec<String> = sources
77+
.iter()
78+
.filter_map(|(path_file, _)| {
79+
let path_str = path_file
80+
.strip_prefix(&project.paths.root)
81+
.unwrap_or(path_file)
82+
.to_path_buf()
83+
.display()
84+
.to_string();
85+
86+
// Skip files that match the given regex pattern.
87+
if let Some(ref regex) = skip {
88+
if regex.is_match(&path_str) {
89+
return None;
90+
}
91+
}
92+
93+
Some(path_str)
94+
})
95+
.collect();
96+
97+
(version.clone(), paths)
98+
})
99+
.filter(|(_, paths)| !paths.is_empty())
100+
.collect();
101+
102+
// Sort by SemVer version.
103+
versions_with_paths.sort_by(|(v1, _), (v2, _)| Version::cmp(v1, v2));
104+
105+
// Skip language if no paths are found after filtering.
106+
if !versions_with_paths.is_empty() {
107+
output.insert(language.to_string(), versions_with_paths);
108+
}
109+
}
110+
111+
if json {
112+
println!("{}", serde_json::to_string(&output)?);
113+
return Ok(());
114+
}
115+
116+
for (language, versions) in &output {
117+
if verbosity < 1 {
118+
println!("{language}:");
119+
} else {
120+
println!("{language}:\n");
121+
}
122+
123+
for (version, paths) in versions {
124+
if verbosity >= 1 {
125+
println!("{version}:");
126+
for (idx, path) in paths.iter().enumerate() {
127+
if idx == paths.len() - 1 {
128+
println!("└── {path}\n");
129+
} else {
130+
println!("├── {path}");
131+
}
132+
}
133+
} else {
134+
println!("- {version}");
135+
}
136+
}
137+
138+
if verbosity < 1 {
139+
println!();
140+
}
141+
}
142+
143+
Ok(())
144+
}
145+
}

crates/forge/bin/cmd/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub mod bind_json;
4444
pub mod build;
4545
pub mod cache;
4646
pub mod clone;
47+
pub mod compiler;
4748
pub mod config;
4849
pub mod coverage;
4950
pub mod create;

crates/forge/bin/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ fn main() -> Result<()> {
118118
ForgeSubcommand::Generate(cmd) => match cmd.sub {
119119
GenerateSubcommands::Test(cmd) => cmd.run(),
120120
},
121+
ForgeSubcommand::Compiler(cmd) => cmd.run(),
121122
ForgeSubcommand::Soldeer(cmd) => utils::block_on(cmd.run()),
122123
ForgeSubcommand::Eip712(cmd) => cmd.run(),
123124
ForgeSubcommand::BindJson(cmd) => cmd.run(),

crates/forge/bin/opts.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use crate::cmd::{
2-
bind::BindArgs, bind_json, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config,
3-
coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs, eip712, flatten, fmt::FmtArgs,
4-
geiger, generate, init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs,
5-
remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update,
2+
bind::BindArgs, bind_json, build::BuildArgs, cache::CacheArgs, clone::CloneArgs,
3+
compiler::CompilerArgs, config, coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs,
4+
eip712, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs,
5+
remappings::RemappingArgs, remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot,
6+
soldeer, test, tree, update,
67
};
78
use clap::{Parser, Subcommand, ValueHint};
89
use forge_script::ScriptArgs;
@@ -152,7 +153,7 @@ pub enum ForgeSubcommand {
152153
/// Generate documentation for the project.
153154
Doc(DocArgs),
154155

155-
/// Function selector utilities
156+
/// Function selector utilities.
156157
#[command(visible_alias = "se")]
157158
Selectors {
158159
#[command(subcommand)]
@@ -162,6 +163,9 @@ pub enum ForgeSubcommand {
162163
/// Generate scaffold files.
163164
Generate(generate::GenerateArgs),
164165

166+
/// Compiler utilities.
167+
Compiler(CompilerArgs),
168+
165169
/// Soldeer dependency manager.
166170
Soldeer(soldeer::SoldeerArgs),
167171

0 commit comments

Comments
 (0)