Skip to content

Commit

Permalink
feat(forge): add compiler subcommand (#7909)
Browse files Browse the repository at this point in the history
* 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 <57450786+DaniPopes@users.noreply.github.com>

* `compiler` command is sync, remove conditions on CI for Vyper / Python installs

* is_jsonlines -> is_json

---------

Co-authored-by: zerosnacks <zerosnacks@protonmail.com>
Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com>
  • Loading branch information
4 people authored Oct 16, 2024
1 parent 3786b27 commit adb6aba
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 18 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/nextest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,10 @@ jobs:
with:
bun-version: latest
- name: Setup Python
if: contains(matrix.name, 'external')
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Install Vyper
if: contains(matrix.name, 'external')
run: pip install vyper~=0.4.0

- name: Forge RPC cache
Expand Down
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ foundry-linking = { path = "crates/linking" }

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

Expand Down
145 changes: 145 additions & 0 deletions crates/forge/bin/cmd/compiler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use clap::{ArgAction, Parser, Subcommand, ValueHint};
use eyre::Result;
use foundry_compilers::Graph;
use foundry_config::Config;
use semver::Version;
use std::{collections::BTreeMap, path::PathBuf};

/// CLI arguments for `forge compiler`.
#[derive(Debug, Parser)]
pub struct CompilerArgs {
#[command(subcommand)]
pub sub: CompilerSubcommands,
}

impl CompilerArgs {
pub fn run(self) -> Result<()> {
match self.sub {
CompilerSubcommands::Resolve(args) => args.run(),
}
}
}

#[derive(Debug, Subcommand)]
pub enum CompilerSubcommands {
/// Retrieves the resolved version(s) of the compiler within the project.
#[command(visible_alias = "r")]
Resolve(ResolveArgs),
}

/// CLI arguments for `forge compiler resolve`.
#[derive(Debug, Parser)]
pub struct ResolveArgs {
/// The root directory
#[arg(long, short, value_hint = ValueHint::DirPath, value_name = "PATH")]
root: Option<PathBuf>,

/// Skip files that match the given regex pattern.
#[arg(long, short, value_name = "REGEX")]
skip: Option<regex::Regex>,

/// Verbosity of the output.
///
/// Pass multiple times to increase the verbosity (e.g. -v, -vv, -vvv).
///
/// Verbosity levels:
/// - 2: Print source paths.
#[arg(long, short, verbatim_doc_comment, action = ArgAction::Count, help_heading = "Display options")]
pub verbosity: u8,

/// Print as JSON.
#[arg(long, short, help_heading = "Display options")]
json: bool,
}

impl ResolveArgs {
pub fn run(self) -> Result<()> {
let Self { root, skip, verbosity, json } = self;

let root = root.unwrap_or_else(|| PathBuf::from("."));
let config = Config::load_with_root(&root);
let project = config.project()?;

let graph = Graph::resolve(&project.paths)?;
let (sources, _) = graph.into_sources_by_version(
project.offline,
&project.locked_versions,
&project.compiler,
)?;

let mut output: BTreeMap<String, Vec<(Version, Vec<String>)>> = BTreeMap::new();

for (language, sources) in sources {
let mut versions_with_paths: Vec<(Version, Vec<String>)> = sources
.iter()
.map(|(version, sources)| {
let paths: Vec<String> = sources
.iter()
.filter_map(|(path_file, _)| {
let path_str = path_file
.strip_prefix(&project.paths.root)
.unwrap_or(path_file)
.to_path_buf()
.display()
.to_string();

// Skip files that match the given regex pattern.
if let Some(ref regex) = skip {
if regex.is_match(&path_str) {
return None;
}
}

Some(path_str)
})
.collect();

(version.clone(), paths)
})
.filter(|(_, paths)| !paths.is_empty())
.collect();

// Sort by SemVer version.
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() {
output.insert(language.to_string(), versions_with_paths);
}
}

if json {
println!("{}", serde_json::to_string(&output)?);
return Ok(());
}

for (language, versions) in &output {
if verbosity < 1 {
println!("{language}:");
} else {
println!("{language}:\n");
}

for (version, paths) in versions {
if verbosity >= 1 {
println!("{version}:");
for (idx, path) in paths.iter().enumerate() {
if idx == paths.len() - 1 {
println!("└── {path}\n");
} else {
println!("├── {path}");
}
}
} else {
println!("- {version}");
}
}

if verbosity < 1 {
println!();
}
}

Ok(())
}
}
1 change: 1 addition & 0 deletions crates/forge/bin/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub mod bind_json;
pub mod build;
pub mod cache;
pub mod clone;
pub mod compiler;
pub mod config;
pub mod coverage;
pub mod create;
Expand Down
1 change: 1 addition & 0 deletions crates/forge/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ fn main() -> Result<()> {
ForgeSubcommand::Generate(cmd) => match cmd.sub {
GenerateSubcommands::Test(cmd) => cmd.run(),
},
ForgeSubcommand::Compiler(cmd) => cmd.run(),
ForgeSubcommand::Soldeer(cmd) => utils::block_on(cmd.run()),
ForgeSubcommand::Eip712(cmd) => cmd.run(),
ForgeSubcommand::BindJson(cmd) => cmd.run(),
Expand Down
14 changes: 9 additions & 5 deletions crates/forge/bin/opts.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::cmd::{
bind::BindArgs, bind_json, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config,
coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs, eip712, flatten, fmt::FmtArgs,
geiger, generate, init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs,
remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot, soldeer, test, tree, update,
bind::BindArgs, bind_json, build::BuildArgs, cache::CacheArgs, clone::CloneArgs,
compiler::CompilerArgs, config, coverage, create::CreateArgs, debug::DebugArgs, doc::DocArgs,
eip712, flatten, fmt::FmtArgs, geiger, generate, init::InitArgs, inspect, install::InstallArgs,
remappings::RemappingArgs, remove::RemoveArgs, selectors::SelectorsSubcommands, snapshot,
soldeer, test, tree, update,
};
use clap::{Parser, Subcommand, ValueHint};
use forge_script::ScriptArgs;
Expand Down Expand Up @@ -152,7 +153,7 @@ pub enum ForgeSubcommand {
/// Generate documentation for the project.
Doc(DocArgs),

/// Function selector utilities
/// Function selector utilities.
#[command(visible_alias = "se")]
Selectors {
#[command(subcommand)]
Expand All @@ -162,6 +163,9 @@ pub enum ForgeSubcommand {
/// Generate scaffold files.
Generate(generate::GenerateArgs),

/// Compiler utilities.
Compiler(CompilerArgs),

/// Soldeer dependency manager.
Soldeer(soldeer::SoldeerArgs),

Expand Down
Loading

0 comments on commit adb6aba

Please sign in to comment.