Skip to content

Commit 47a9a20

Browse files
committed
feat: add build args
1 parent 4f3075c commit 47a9a20

File tree

8 files changed

+310
-95
lines changed

8 files changed

+310
-95
lines changed

bin/pcl/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use pcl_core::{
1414
ConfigArgs,
1515
},
1616
};
17-
use pcl_phoundry::test::PhorgeTest;
17+
use pcl_phoundry::phorge_test::PhorgeTest;
1818
use serde_json::json;
1919

2020
const VERSION_MESSAGE: &str = concat!(

crates/core/tests/common/da_store_harness.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use pcl_core::{
1010
assertion_da::DaStoreArgs,
1111
error::DaSubmitError,
1212
};
13-
use pcl_phoundry::phorge::BuildAndFlattenArgs;
13+
use pcl_phoundry::build_and_flatten::BuildAndFlattenArgs;
1414
use std::{
1515
collections::HashMap,
1616
path::PathBuf,

crates/phoundry/src/build.rs

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
use clap::{
2+
Parser,
3+
ValueHint,
4+
};
5+
use foundry_cli::opts::{
6+
BuildOpts,
7+
ProjectPathOpts,
8+
};
9+
10+
use std::path::PathBuf;
11+
12+
use crate::compile::compile;
13+
use crate::error::PhoundryError;
14+
15+
/// Command-line arguments for building assertion contracts and tests.
16+
#[derive(Debug, Default, Parser)]
17+
#[clap(about = "Build contracts using Phorge")]
18+
pub struct BuildArgs {
19+
/// Root directory of the project
20+
#[clap(
21+
short = 'r',
22+
long,
23+
value_hint = ValueHint::DirPath,
24+
help = "Root directory of the project"
25+
)]
26+
pub root: Option<PathBuf>,
27+
}
28+
29+
impl BuildArgs {
30+
/// Builds the assertion contract and tests
31+
///
32+
/// # Returns
33+
///
34+
/// - `Ok(())`
35+
/// - `Err(PhoundryError)` if any step in the process fails
36+
pub fn run(&self) -> Result<(), Box<PhoundryError>> {
37+
let build_cmd = BuildOpts {
38+
project_paths: ProjectPathOpts {
39+
root: self.root.clone(),
40+
// FIXME(Odysseas): this essentially hard-codes the location of the assertions to live in
41+
// assertions/src
42+
contracts: Some(PathBuf::from("assertions/src")),
43+
..Default::default()
44+
},
45+
..Default::default()
46+
};
47+
48+
foundry_cli::utils::load_dotenv();
49+
50+
compile(build_cmd)?;
51+
Ok(())
52+
}
53+
}
54+
55+
#[cfg(test)]
56+
mod tests {
57+
use super::*;
58+
use std::fs;
59+
use tempfile::TempDir;
60+
61+
// Helper function to create a temporary Solidity project with valid contracts
62+
fn setup_valid_test_project() -> (TempDir, PathBuf) {
63+
let temp_dir = TempDir::new().unwrap();
64+
let project_root = temp_dir.path().join("test_project");
65+
fs::create_dir_all(&project_root).unwrap();
66+
67+
// Create assertions/src directory structure
68+
let contract_dir = project_root.join("assertions").join("src");
69+
fs::create_dir_all(&contract_dir).unwrap();
70+
71+
// Create a valid test contract
72+
let contract_path = contract_dir.join("ValidContract.sol");
73+
fs::write(
74+
&contract_path,
75+
r#"// SPDX-License-Identifier: MIT
76+
pragma solidity ^0.8.0;
77+
78+
contract ValidContract {
79+
function test() public pure returns (bool) {
80+
return true;
81+
}
82+
}"#,
83+
)
84+
.unwrap();
85+
86+
(temp_dir, project_root)
87+
}
88+
89+
// Helper function to create a temporary Solidity project with compilation errors
90+
fn setup_invalid_test_project() -> (TempDir, PathBuf) {
91+
let temp_dir = TempDir::new().unwrap();
92+
let project_root = temp_dir.path().join("test_project");
93+
fs::create_dir_all(&project_root).unwrap();
94+
95+
// Create assertions/src directory structure
96+
let contract_dir = project_root.join("assertions").join("src");
97+
fs::create_dir_all(&contract_dir).unwrap();
98+
99+
// Create a contract with compilation errors
100+
let contract_path = contract_dir.join("InvalidContract.sol");
101+
fs::write(
102+
&contract_path,
103+
r#"// SPDX-License-Identifier: MIT
104+
pragma solidity ^0.8.0;
105+
106+
contract InvalidContract {
107+
// Missing semicolon - syntax error
108+
uint256 public value = 42
109+
110+
// Invalid function syntax - missing parentheses
111+
function test public pure returns (bool) {
112+
// Type mismatch error
113+
return "not a boolean";
114+
}
115+
116+
// Undefined variable error
117+
function anotherTest() public pure returns (uint256) {
118+
return undefinedVariable;
119+
}
120+
121+
// Missing closing brace
122+
}"#,
123+
)
124+
.unwrap();
125+
126+
(temp_dir, project_root)
127+
}
128+
129+
// Helper function to create an empty project (no source files)
130+
fn setup_empty_test_project() -> (TempDir, PathBuf) {
131+
let temp_dir = TempDir::new().unwrap();
132+
let project_root = temp_dir.path().join("test_project");
133+
fs::create_dir_all(&project_root).unwrap();
134+
135+
// Create assertions/src directory but leave it empty
136+
let contract_dir = project_root.join("assertions").join("src");
137+
fs::create_dir_all(&contract_dir).unwrap();
138+
139+
(temp_dir, project_root)
140+
}
141+
142+
#[test]
143+
fn test_build_args_new() {
144+
let args = BuildArgs { root: None };
145+
146+
assert!(args.root.is_none());
147+
}
148+
149+
#[test]
150+
fn test_build_args_with_root() {
151+
let root_path = PathBuf::from("/test/path");
152+
let args = BuildArgs {
153+
root: Some(root_path.clone()),
154+
};
155+
156+
assert_eq!(args.root, Some(root_path));
157+
}
158+
159+
#[test]
160+
fn test_compilation_with_invalid_contract() {
161+
let (_temp_dir, project_root) = setup_invalid_test_project();
162+
163+
let args = BuildArgs {
164+
root: Some(project_root),
165+
};
166+
167+
let result = args.run();
168+
169+
// Compilation should fail due to syntax errors
170+
assert!(
171+
result.is_err(),
172+
"Expected compilation to fail with invalid contract"
173+
);
174+
175+
// Verify it's a compilation error
176+
if let Err(error) = result {
177+
// The error should be a compilation error or related error type
178+
// We can't check the exact error type without knowing the PhoundryError variants,
179+
// but the compilation should definitely fail
180+
println!("Compilation failed as expected with error: {:?}", error);
181+
}
182+
}
183+
184+
#[test]
185+
fn test_compilation_with_empty_directory() {
186+
let (_temp_dir, project_root) = setup_empty_test_project();
187+
188+
let args = BuildArgs {
189+
root: Some(project_root),
190+
};
191+
192+
let result = args.run();
193+
194+
// Compilation should fail due to no source files
195+
assert!(
196+
result.is_err(),
197+
"Expected compilation to fail with empty directory"
198+
);
199+
200+
if let Err(error) = result {
201+
println!("Compilation failed as expected with error: {:?}", error);
202+
}
203+
}
204+
205+
#[test]
206+
fn test_compilation_with_nonexistent_directory() {
207+
let temp_dir = TempDir::new().unwrap();
208+
let nonexistent_path = temp_dir.path().join("nonexistent_project");
209+
210+
let args = BuildArgs {
211+
root: Some(nonexistent_path),
212+
};
213+
214+
let result = args.run();
215+
216+
// Compilation should fail due to nonexistent directory
217+
assert!(
218+
result.is_err(),
219+
"Expected compilation to fail with nonexistent directory"
220+
);
221+
222+
if let Err(error) = result {
223+
println!("Compilation failed as expected with error: {:?}", error);
224+
}
225+
}
226+
227+
#[tokio::test(flavor = "multi_thread")]
228+
async fn test_build_integration_with_valid_contract() {
229+
let (_temp_dir, project_root) = setup_valid_test_project();
230+
231+
let args = BuildArgs {
232+
root: Some(project_root),
233+
};
234+
235+
let result = args.run();
236+
237+
// This test might succeed or fail depending on the environment and dependencies
238+
// In a real test environment with proper setup, it should succeed with valid contracts
239+
match result {
240+
Ok(_) => println!("Compilation succeeded as expected"),
241+
Err(error) => {
242+
println!(
243+
"Compilation failed (might be expected in test environment): {:?}",
244+
error
245+
)
246+
}
247+
}
248+
}
249+
}

crates/phoundry/src/build_and_flatten.rs

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,13 @@ use clap::{
22
Parser,
33
ValueHint,
44
};
5-
use color_eyre::Report;
6-
use forge::{
7-
cmd::{
8-
build::BuildArgs,
9-
test::TestArgs,
10-
},
11-
opts::{
12-
Forge,
13-
ForgeSubcommand,
14-
},
15-
};
165
use foundry_cli::{
176
opts::{
187
BuildOpts,
198
ProjectPathOpts,
209
},
2110
utils::LoadConfig,
2211
};
23-
use foundry_common::compile::ProjectCompiler;
2412
use foundry_compilers::{
2513
flatten::{
2614
Flattener,
@@ -33,12 +21,8 @@ use foundry_compilers::{
3321

3422
use alloy_json_abi::JsonAbi;
3523

36-
use foundry_config::{
37-
error::ExtractConfigError,
38-
find_project_root,
39-
};
24+
use foundry_config::find_project_root;
4025
use std::path::PathBuf;
41-
use tokio::task::spawn_blocking;
4226

4327
use crate::error::PhoundryError;
4428

@@ -151,8 +135,8 @@ impl BuildAndFlattenArgs {
151135

152136
/// Builds the project and returns the compilation output.
153137
fn build(&self) -> Result<ProjectCompileOutput, Box<PhoundryError>> {
154-
let build_cmd = BuildArgs {
155-
build: BuildOpts {
138+
let build_opts =
139+
BuildOpts {
156140
project_paths: ProjectPathOpts {
157141
root: self.root.clone(),
158142
// FIXME(Odysseas): this essentially hard-codes the location of the assertions to live in
@@ -161,41 +145,9 @@ impl BuildAndFlattenArgs {
161145
..Default::default()
162146
},
163147
..Default::default()
164-
},
165-
..Default::default()
166-
};
148+
};
167149

168-
let config = build_cmd.load_config()?;
169-
170-
let project = config.project().map_err(PhoundryError::SolcError)?;
171-
let contracts = project.sources_path();
172-
173-
match std::fs::read_dir(contracts) {
174-
Ok(mut files) => {
175-
// Check if the directory is empty
176-
if files.next().is_none() {
177-
return Err(Box::new(PhoundryError::NoSourceFilesFound));
178-
}
179-
}
180-
Err(_) => {
181-
return Err(Box::new(PhoundryError::DirectoryNotFound(
182-
contracts.to_path_buf(),
183-
)));
184-
}
185-
}
186-
187-
let compiler = ProjectCompiler::new()
188-
.dynamic_test_linking(config.dynamic_test_linking)
189-
.print_names(build_cmd.names)
190-
.print_sizes(build_cmd.sizes)
191-
.ignore_eip_3860(build_cmd.ignore_eip_3860)
192-
.bail(true)
193-
.quiet(true);
194-
195-
let res = compiler
196-
.compile(&project)
197-
.map_err(PhoundryError::CompilationError)?;
198-
Ok(res)
150+
crate::compile::compile(build_opts)
199151
}
200152

201153
/// Flattens the contract source code.
@@ -234,7 +186,6 @@ impl BuildAndFlattenArgs {
234186
}
235187
}
236188

237-
238189
#[cfg(test)]
239190
mod tests {
240191
use super::*;

0 commit comments

Comments
 (0)