diff --git a/Cargo.lock b/Cargo.lock index 0076ecc4210dc..7d52542ea9b2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3542,6 +3542,86 @@ dependencies = [ "yansi 1.0.1", ] +[[package]] +name = "forge-cmd" +version = "0.2.0" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types", + "alloy-signer", + "alloy-signer-wallet", + "alloy-transport", + "anvil", + "async-trait", + "axum", + "clap", + "clap_complete", + "clap_complete_fig", + "comfy-table", + "const-hex", + "criterion", + "dialoguer", + "dunce", + "ethers-contract", + "evm-disassembler", + "eyre", + "forge", + "forge-doc", + "forge-fmt", + "forge-script", + "forge-verify", + "foundry-block-explorers", + "foundry-cli", + "foundry-common", + "foundry-compilers", + "foundry-config", + "foundry-debugger", + "foundry-evm", + "foundry-test-utils", + "foundry-wallets", + "futures", + "globset", + "hyper 1.3.1", + "indicatif", + "itertools 0.12.1", + "mockall", + "once_cell", + "opener", + "parking_lot", + "paste", + "path-slash", + "pretty_assertions", + "rayon", + "regex", + "reqwest 0.12.4", + "rustc-hash", + "semver 1.0.22", + "serde", + "serde_json", + "similar", + "solang-parser", + "strum", + "svm-rs 0.5.2", + "tempfile", + "thiserror", + "tikv-jemallocator", + "tokio", + "toml 0.8.12", + "toml_edit 0.22.12", + "tower-http", + "tracing", + "tracing-subscriber", + "vergen", + "watchexec", + "yansi 1.0.1", +] + [[package]] name = "forge-doc" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 3513cf6c18e5f..52f4aceb51079 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "crates/evm/traces/", "crates/fmt/", "crates/forge/", + "crates/forge/bin", "crates/macros/", "crates/test-utils/", ] diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9d7b2777d71e7..deaef0944dca4 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -10,9 +10,9 @@ license.workspace = true homepage.workspace = true repository.workspace = true -[[bin]] -name = "forge" -path = "bin/main.rs" +#[[bin]] converted to a crate in the workspace to expose code +#name = "forge" +#path = "bin/main.rs" [build-dependencies] vergen = { workspace = true, default-features = false, features = [ diff --git a/crates/forge/bin/Cargo.toml b/crates/forge/bin/Cargo.toml new file mode 100644 index 0000000000000..950cb62f7ad87 --- /dev/null +++ b/crates/forge/bin/Cargo.toml @@ -0,0 +1,136 @@ +[package] +name = "forge-cmd" +description = "Forge command line interface" + +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lib] +#name = "forge_cmd" +path = "cmd/lib.rs" +#name = "forge" + +[build-dependencies] +vergen = { workspace = true, default-features = false, features = [ + "build", + "git", + "gitcl", +] } + +[dependencies] +# lib +foundry-block-explorers = { workspace = true, features = ["foundry-compilers"] } +foundry-common.workspace = true +foundry-compilers = { workspace = true, features = ["full"] } +foundry-config.workspace = true +foundry-evm.workspace = true +foundry-wallets.workspace = true +#foundry-linking.workspace = true +# +ethers-contract = { workspace = true, features = ["abigen"] } +# +#revm-inspectors.workspace = true +# +comfy-table = "7" +eyre.workspace = true +#proptest = "1" +rayon = "1" +serde.workspace = true +tracing.workspace = true +yansi.workspace = true +#humantime-serde = "1.1.1" + +# bin +forge.workspace = true +forge-doc.workspace = true +forge-fmt.workspace = true +forge-verify.workspace = true +forge-script.workspace = true +foundry-cli.workspace = true +foundry-debugger.workspace = true + +alloy-dyn-abi.workspace = true +alloy-json-abi.workspace = true +alloy-primitives = { workspace = true, features = ["serde"] } +alloy-rpc-types.workspace = true +alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } +alloy-network.workspace = true +alloy-transport.workspace = true +alloy-signer.workspace = true +alloy-consensus.workspace = true +alloy-chains.workspace = true + +async-trait = "0.1" +clap = { version = "4", features = ["derive", "env", "unicode", "wrap_help"] } +clap_complete = "4" +clap_complete_fig = "4" +dialoguer = { version = "0.11", default-features = false } +dunce = "1" +futures = "0.3" +hex.workspace = true +indicatif = "0.17" +itertools.workspace = true +once_cell = "1" +parking_lot = "0.12" +regex = { version = "1", default-features = false } +reqwest = { workspace = true, features = ["json"] } +semver = "1" +serde_json.workspace = true +similar = { version = "2", features = ["inline"] } +solang-parser.workspace = true +strum = { workspace = true, features = ["derive"] } +thiserror = "1" +tokio = { version = "1", features = ["time"] } +toml = { version = "0.8", features = ["preserve_order"] } +toml_edit = "0.22.4" +watchexec = "2.3.2" +evm-disassembler.workspace = true +rustc-hash.workspace = true + +# doc server +axum = { workspace = true, features = ["ws"] } +hyper.workspace = true +tower-http = { workspace = true, features = ["fs"] } +opener = "0.6" + +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + +[dev-dependencies] +anvil.workspace = true +foundry-test-utils.workspace = true + +mockall = "0.12" +criterion = "0.5" +globset = "0.4" +paste = "1.0" +path-slash = "0.2" +pretty_assertions.workspace = true +svm = { package = "svm-rs", version = "0.5", default-features = false, features = [ + "rustls", +] } +tempfile.workspace = true +tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] } + +alloy-signer-wallet.workspace = true + +[features] +default = ["rustls"] +rustls = [ + "foundry-cli/rustls", + "foundry-wallets/rustls", + "reqwest/rustls-tls", + "reqwest/rustls-tls-native-roots", +] +openssl = ["foundry-cli/openssl", "reqwest/default-tls"] +asm-keccak = ["alloy-primitives/asm-keccak"] +jemalloc = ["dep:tikv-jemallocator"] + +#[[bench]] +#name = "test" +#harness = false diff --git a/crates/forge/bin/cmd/lib.rs b/crates/forge/bin/cmd/lib.rs new file mode 100644 index 0000000000000..b68eedb827288 --- /dev/null +++ b/crates/forge/bin/cmd/lib.rs @@ -0,0 +1,9 @@ +#[path = "mod.rs"] +mod cmd; +use cmd::{install, watch}; + +mod test; +pub use test::{FilterArgs, ProjectPathsAwareFilter}; + +#[macro_use] +extern crate tracing; diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index ed0c1305cb7bc..3c0101af7e071 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -1,4 +1,4 @@ -use super::{install, test::filter::ProjectPathsAwareFilter, watch::WatchArgs}; +use super::{install, watch::WatchArgs}; use alloy_primitives::U256; use clap::Parser; use eyre::Result; @@ -44,7 +44,7 @@ mod filter; mod summary; use summary::TestSummaryReporter; -pub use filter::FilterArgs; +pub use filter::{FilterArgs, ProjectPathsAwareFilter}; use forge::traces::render_trace_arena; // Loads project's figment and merges the build cli arguments into it diff --git a/crates/forge/bin/cmd/test/summary.rs b/crates/forge/bin/cmd/test/summary.rs index 561ea6b6824c6..a8efe117b9c5f 100644 --- a/crates/forge/bin/cmd/test/summary.rs +++ b/crates/forge/bin/cmd/test/summary.rs @@ -1,4 +1,4 @@ -use crate::cmd::test::TestOutcome; +use forge::result::TestOutcome; use comfy_table::{ modifiers::UTF8_ROUND_CORNERS, Attribute, Cell, CellAlignment, Color, Row, Table, }; diff --git a/crates/forge/bin/main.rs b/crates/forge/bin/main.rs deleted file mode 100644 index 1c7095026e294..0000000000000 --- a/crates/forge/bin/main.rs +++ /dev/null @@ -1,136 +0,0 @@ -#[macro_use] -extern crate tracing; - -use clap::{CommandFactory, Parser}; -use clap_complete::generate; -use eyre::Result; -use foundry_cli::{handler, utils}; -use foundry_evm::inspectors::cheatcodes::{set_execution_context, ForgeContext}; - -mod cmd; -use cmd::{cache::CacheSubcommands, generate::GenerateSubcommands, watch}; - -mod opts; -use opts::{Forge, ForgeSubcommand}; - -#[cfg(all(feature = "jemalloc", unix))] -#[global_allocator] -static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; - -fn main() -> Result<()> { - handler::install(); - utils::load_dotenv(); - utils::subscriber(); - utils::enable_paint(); - - let opts = Forge::parse(); - init_execution_context(&opts.cmd); - - match opts.cmd { - ForgeSubcommand::Test(cmd) => { - if cmd.is_watch() { - utils::block_on(watch::watch_test(cmd)) - } else { - let outcome = utils::block_on(cmd.run())?; - outcome.ensure_ok() - } - } - ForgeSubcommand::Script(cmd) => { - // install the shell before executing the command - foundry_common::shell::set_shell(foundry_common::shell::Shell::from_args( - cmd.opts.silent, - cmd.json, - ))?; - utils::block_on(cmd.run_script()) - } - ForgeSubcommand::Coverage(cmd) => utils::block_on(cmd.run()), - ForgeSubcommand::Bind(cmd) => cmd.run(), - ForgeSubcommand::Build(cmd) => { - if cmd.is_watch() { - utils::block_on(watch::watch_build(cmd)) - } else { - cmd.run().map(|_| ()) - } - } - ForgeSubcommand::Debug(cmd) => utils::block_on(cmd.run()), - ForgeSubcommand::VerifyContract(args) => utils::block_on(args.run()), - ForgeSubcommand::VerifyCheck(args) => utils::block_on(args.run()), - ForgeSubcommand::Clone(cmd) => utils::block_on(cmd.run()), - ForgeSubcommand::Cache(cmd) => match cmd.sub { - CacheSubcommands::Clean(cmd) => cmd.run(), - CacheSubcommands::Ls(cmd) => cmd.run(), - }, - ForgeSubcommand::Create(cmd) => utils::block_on(cmd.run()), - ForgeSubcommand::Update(cmd) => cmd.run(), - ForgeSubcommand::Install(cmd) => cmd.run(), - ForgeSubcommand::Remove(cmd) => cmd.run(), - ForgeSubcommand::Remappings(cmd) => cmd.run(), - ForgeSubcommand::Init(cmd) => cmd.run(), - ForgeSubcommand::Completions { shell } => { - generate(shell, &mut Forge::command(), "forge", &mut std::io::stdout()); - Ok(()) - } - ForgeSubcommand::GenerateFigSpec => { - clap_complete::generate( - clap_complete_fig::Fig, - &mut Forge::command(), - "forge", - &mut std::io::stdout(), - ); - Ok(()) - } - ForgeSubcommand::Clean { root } => { - let config = utils::load_config_with_root(root); - config.project()?.cleanup()?; - Ok(()) - } - ForgeSubcommand::Snapshot(cmd) => { - if cmd.is_watch() { - utils::block_on(watch::watch_snapshot(cmd)) - } else { - utils::block_on(cmd.run()) - } - } - ForgeSubcommand::Fmt(cmd) => cmd.run(), - ForgeSubcommand::Config(cmd) => cmd.run(), - ForgeSubcommand::Flatten(cmd) => cmd.run(), - ForgeSubcommand::Inspect(cmd) => cmd.run(), - ForgeSubcommand::Tree(cmd) => cmd.run(), - ForgeSubcommand::Geiger(cmd) => { - let check = cmd.check; - let n = cmd.run()?; - if check && n > 0 { - std::process::exit(n as i32); - } - Ok(()) - } - ForgeSubcommand::Doc(cmd) => cmd.run(), - ForgeSubcommand::Selectors { command } => utils::block_on(command.run()), - ForgeSubcommand::Generate(cmd) => match cmd.sub { - GenerateSubcommands::Test(cmd) => cmd.run(), - }, - ForgeSubcommand::VerifyBytecode(cmd) => utils::block_on(cmd.run()), - } -} - -/// Set the program execution context based on `forge` subcommand used. -/// The execution context can be set only once per program, and it can be checked by using -/// cheatcodes. -fn init_execution_context(subcommand: &ForgeSubcommand) { - let context = match subcommand { - ForgeSubcommand::Test(_) => ForgeContext::Test, - ForgeSubcommand::Coverage(_) => ForgeContext::Coverage, - ForgeSubcommand::Snapshot(_) => ForgeContext::Snapshot, - ForgeSubcommand::Script(cmd) => { - if cmd.broadcast { - ForgeContext::ScriptBroadcast - } else if cmd.resume { - ForgeContext::ScriptResume - } else { - ForgeContext::ScriptDryRun - } - } - _ => ForgeContext::Unknown, - }; - set_execution_context(context); -} diff --git a/crates/forge/bin/opts.rs b/crates/forge/bin/opts.rs deleted file mode 100644 index 6ca78da0f44e1..0000000000000 --- a/crates/forge/bin/opts.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::cmd::{ - bind::BindArgs, build::BuildArgs, cache::CacheArgs, clone::CloneArgs, config, coverage, - create::CreateArgs, debug::DebugArgs, doc::DocArgs, flatten, fmt::FmtArgs, geiger, generate, - init::InitArgs, inspect, install::InstallArgs, remappings::RemappingArgs, remove::RemoveArgs, - selectors::SelectorsSubcommands, snapshot, test, tree, update, -}; -use clap::{Parser, Subcommand, ValueHint}; -use forge_script::ScriptArgs; -use forge_verify::{bytecode::VerifyBytecodeArgs, VerifyArgs, VerifyCheckArgs}; -use std::path::PathBuf; - -const VERSION_MESSAGE: &str = concat!( - env!("CARGO_PKG_VERSION"), - " (", - env!("VERGEN_GIT_SHA"), - " ", - env!("VERGEN_BUILD_TIMESTAMP"), - ")" -); - -/// Build, test, fuzz, debug and deploy Solidity contracts. -#[derive(Parser)] -#[command( - name = "forge", - version = VERSION_MESSAGE, - after_help = "Find more information in the book: http://book.getfoundry.sh/reference/forge/forge.html", - next_display_order = None, -)] -pub struct Forge { - #[command(subcommand)] - pub cmd: ForgeSubcommand, -} - -#[derive(Subcommand)] -#[allow(clippy::large_enum_variant)] -pub enum ForgeSubcommand { - /// Run the project's tests. - #[command(visible_alias = "t")] - Test(test::TestArgs), - - /// Run a smart contract as a script, building transactions that can be sent onchain. - Script(ScriptArgs), - - /// Generate coverage reports. - Coverage(coverage::CoverageArgs), - - /// Generate Rust bindings for smart contracts. - #[command(alias = "bi")] - Bind(BindArgs), - - /// Build the project's smart contracts. - #[command(visible_aliases = ["b", "compile"])] - Build(BuildArgs), - - /// Clone a contract from Etherscan. - Clone(CloneArgs), - - /// Debugs a single smart contract as a script. - #[command(visible_alias = "d")] - Debug(DebugArgs), - - /// Update one or multiple dependencies. - /// - /// If no arguments are provided, then all dependencies are updated. - #[command(visible_alias = "u")] - Update(update::UpdateArgs), - - /// Install one or multiple dependencies. - /// - /// If no arguments are provided, then existing dependencies will be installed. - #[command(visible_alias = "i")] - Install(InstallArgs), - - /// Remove one or multiple dependencies. - #[command(visible_alias = "rm")] - Remove(RemoveArgs), - - /// Get the automatically inferred remappings for the project. - #[command(visible_alias = "re")] - Remappings(RemappingArgs), - - /// Verify smart contracts on Etherscan. - #[command(visible_alias = "v")] - VerifyContract(VerifyArgs), - - /// Check verification status on Etherscan. - #[command(visible_alias = "vc")] - VerifyCheck(VerifyCheckArgs), - - /// Deploy a smart contract. - #[command(visible_alias = "c")] - Create(CreateArgs), - - /// Create a new Forge project. - Init(InitArgs), - - /// Generate shell completions script. - #[command(visible_alias = "com")] - Completions { - #[arg(value_enum)] - shell: clap_complete::Shell, - }, - - /// Generate Fig autocompletion spec. - #[command(visible_alias = "fig")] - GenerateFigSpec, - - /// Remove the build artifacts and cache directories. - #[command(visible_alias = "cl")] - Clean { - /// The project's root path. - /// - /// By default root of the Git repository, if in one, - /// or the current working directory. - #[arg(long, value_hint = ValueHint::DirPath, value_name = "PATH")] - root: Option, - }, - - /// Manage the Foundry cache. - Cache(CacheArgs), - - /// Create a snapshot of each test's gas usage. - #[command(visible_alias = "s")] - Snapshot(snapshot::SnapshotArgs), - - /// Display the current config. - #[command(visible_alias = "co")] - Config(config::ConfigArgs), - - /// Flatten a source file and all of its imports into one file. - #[command(visible_alias = "f")] - Flatten(flatten::FlattenArgs), - - /// Format Solidity source files. - Fmt(FmtArgs), - - /// Get specialized information about a smart contract. - #[command(visible_alias = "in")] - Inspect(inspect::InspectArgs), - - /// Display a tree visualization of the project's dependency graph. - #[command(visible_alias = "tr")] - Tree(tree::TreeArgs), - - /// Detects usage of unsafe cheat codes in a project and its dependencies. - Geiger(geiger::GeigerArgs), - - /// Generate documentation for the project. - Doc(DocArgs), - - /// Function selector utilities - #[command(visible_alias = "se")] - Selectors { - #[command(subcommand)] - command: SelectorsSubcommands, - }, - - /// Generate scaffold files. - Generate(generate::GenerateArgs), - - /// Verify the deployed bytecode against its source. - #[clap(visible_alias = "vb")] - VerifyBytecode(VerifyBytecodeArgs), -} - -#[cfg(test)] -mod tests { - use super::*; - use clap::CommandFactory; - - #[test] - fn verify_cli() { - Forge::command().debug_assert(); - } -}