Skip to content

Commit e222f18

Browse files
committed
Refactor USC
1 parent 403eb8e commit e222f18

File tree

8 files changed

+109
-119
lines changed

8 files changed

+109
-119
lines changed

Cargo.lock

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

crates/forge/src/compatibility_check.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ mod tests {
143143
fixture::{FileWriteStr, PathChild},
144144
};
145145
use scarb_api::ScarbCommand;
146-
use universal_sierra_compiler_api::UniversalSierraCompilerCommand;
147146

148147
#[test]
149148
fn happy_case() {
@@ -178,7 +177,7 @@ mod tests {
178177
});
179178
requirements_checker.add_requirement(Requirement {
180179
name: "Universal Sierra Compiler".to_string(),
181-
command: RefCell::new(UniversalSierraCompilerCommand::new().arg("--version").command()),
180+
command: RefCell::new(universal_sierra_compiler_api::version_command().unwrap()),
182181
minimal_version: Version::new(2, 0, 0),
183182
minimal_recommended_version: None,
184183
helper_text: "Reinstall `snforge` using the same installation method or follow instructions from https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#universal-sierra-compiler-update".to_string(),
@@ -300,7 +299,7 @@ mod tests {
300299
});
301300
requirements_checker.add_requirement(Requirement {
302301
name: "Universal Sierra Compiler".to_string(),
303-
command: RefCell::new(UniversalSierraCompilerCommand::new().arg("--version").command()),
302+
command: RefCell::new(universal_sierra_compiler_api::version_command().unwrap()),
304303
minimal_version: Version::new(2, 4, 0),
305304
minimal_recommended_version: None,
306305
helper_text: "Reinstall `snforge` using the same installation method or follow instructions from https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#universal-sierra-compiler-update".to_string(),

crates/forge/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use std::ffi::OsString;
1818
use std::sync::Arc;
1919
use std::{fs, num::NonZeroU32, thread::available_parallelism};
2020
use tokio::runtime::Builder;
21-
use universal_sierra_compiler_api::UniversalSierraCompilerCommand;
2221

2322
pub mod block_number_map;
2423
mod clean;
@@ -362,7 +361,7 @@ fn check_requirements(output_on_success: bool, ui: &UI) -> Result<()> {
362361

363362
requirements_checker.add_requirement(Requirement {
364363
name: "Universal Sierra Compiler".to_string(),
365-
command: RefCell::new(UniversalSierraCompilerCommand::new().arg("--version").command()),
364+
command: RefCell::new(universal_sierra_compiler_api::version_command()?),
366365
minimal_version: MINIMAL_USC_VERSION,
367366
minimal_recommended_version: None,
368367
helper_text: "Reinstall `snforge` using the same installation method or follow instructions from https://foundry-rs.github.io/starknet-foundry/getting-started/installation.html#universal-sierra-compiler-update".to_string(),

crates/shared/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ clap_complete.workspace = true
1919
cairo-vm.workspace = true
2020
num-traits.workspace = true
2121
foundry-ui ={ path = "../foundry-ui" }
22+
thiserror = "2.0.17"
2223

2324
[features]
2425
testing = []

crates/shared/src/command.rs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,42 @@
1-
use anyhow::{Context, Ok, Result, bail};
2-
use std::process::{Command, Output};
1+
use std::io;
2+
use std::process::{Command, ExitStatus, Output};
3+
use thiserror::Error;
34

5+
/// Error type for command execution failures
6+
#[derive(Debug, Error)]
7+
pub enum CommandError {
8+
#[error("Failed to run {command}: {source}")]
9+
IoError {
10+
command: String,
11+
#[source]
12+
source: io::Error,
13+
},
14+
15+
#[error("Command {command} failed with status {status}")]
16+
FailedStatus { command: String, status: ExitStatus },
17+
}
18+
19+
/// Trait extension for Command to check output status
420
pub trait CommandExt {
5-
fn output_checked(&mut self) -> Result<Output>;
21+
fn output_checked(&mut self) -> Result<Output, CommandError>;
622
}
723

824
impl CommandExt for Command {
9-
fn output_checked(&mut self) -> Result<Output> {
25+
fn output_checked(&mut self) -> Result<Output, CommandError> {
1026
let command = self.get_program().to_string_lossy().to_string();
1127

12-
let output = self
13-
.output()
14-
.with_context(|| format!("Failed to run {command}"))?;
15-
16-
if !output.status.success() {
17-
bail!("Command {command} failed with status {}", output.status);
28+
match self.output() {
29+
Ok(output) => {
30+
if output.status.success() {
31+
Ok(output)
32+
} else {
33+
Err(CommandError::FailedStatus {
34+
command,
35+
status: output.status,
36+
})
37+
}
38+
}
39+
Err(source) => Err(CommandError::IoError { command, source }),
1840
}
19-
20-
Ok(output)
2141
}
2242
}

crates/universal-sierra-compiler-api/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ num-bigint.workspace = true
1414
cairo-lang-casm.workspace = true
1515
camino.workspace = true
1616
tracing.workspace = true
17+
strum_macros.workspace = true
18+
thiserror.workspace = true
Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,72 @@
1-
use anyhow::Context;
2-
use std::env;
3-
use std::ffi::{OsStr, OsString};
4-
use std::path::PathBuf;
5-
use std::process::{Command, Stdio};
1+
use shared::command::{CommandError, CommandExt};
2+
use std::process::Output;
3+
use std::{
4+
env,
5+
ffi::OsStr,
6+
process::{Command, Stdio},
7+
};
8+
use thiserror::Error;
9+
10+
/// Represents possible errors when working with USC commands.
11+
12+
#[derive(Debug, Error)]
13+
pub enum USCError {
14+
#[error(
15+
"`universal-sierra-compiler` binary not available. \
16+
Make sure it is installed and available in PATH or set via UNIVERSAL_SIERRA_COMPILER."
17+
)]
18+
NotFound(#[source] which::Error),
19+
20+
#[error(
21+
"Error while compiling Sierra. \
22+
Make sure you have the latest universal-sierra-compiler binary installed. \
23+
Contact us if it doesn't help."
24+
)]
25+
RunFailed(#[source] CommandError),
26+
}
627

728
/// A builder for `universal-sierra-compiler` command invocation.
8-
#[derive(Clone, Debug, Default)]
9-
pub struct UniversalSierraCompilerCommand {
10-
args: Vec<OsString>,
11-
current_dir: Option<PathBuf>,
12-
inherit_stderr: bool,
29+
#[derive(Debug)]
30+
pub struct USCInternalCommand {
31+
inner: Command,
1332
}
1433

15-
impl UniversalSierraCompilerCommand {
34+
impl USCInternalCommand {
1635
/// Creates a default `universal-sierra-compiler` command,
1736
/// which will look for `universal-sierra-compiler` in `$PATH`
18-
#[must_use]
19-
pub fn new() -> Self {
20-
Self::default()
21-
}
22-
23-
/// Ensures that `universal-sierra-compiler` binary is available in the system.
24-
pub fn ensure_available() -> anyhow::Result<()> {
25-
which::which(UniversalSierraCompilerCommand::binary_path())
26-
.context("Cannot find `universal-sierra-compiler` binary. \
27-
Make sure you have USC installed https://github.com/software-mansion/universal-sierra-compiler \
28-
and added to PATH (or set at UNIVERSAL_SIERRA_COMPILER env var)"
29-
)?;
30-
Ok(())
31-
}
32-
33-
/// Current directory of the `universal-sierra-compiler` process.
34-
pub fn current_dir(&mut self, path: impl Into<PathBuf>) -> &mut Self {
35-
self.current_dir = Some(path.into());
36-
self
37-
}
38-
39-
/// Inherit standard error, i.e. show USC errors in this process's standard error.
40-
pub fn inherit_stderr(&mut self) -> &mut Self {
41-
self.inherit_stderr = true;
42-
self
37+
pub fn new() -> Result<Self, USCError> {
38+
ensure_available()?;
39+
let mut cmd = Command::new(binary_path());
40+
cmd.stderr(Stdio::inherit());
41+
Ok(Self { inner: cmd })
4342
}
4443

4544
/// Adds an argument to pass to `universal-sierra-compiler`.
46-
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
47-
self.args.push(arg.as_ref().to_os_string());
48-
self
49-
}
50-
51-
/// Adds multiple arguments to pass to `universal-sierra-compiler`.
52-
pub fn args<I, S>(&mut self, args: I) -> &mut Self
53-
where
54-
I: IntoIterator<Item = S>,
55-
S: AsRef<OsStr>,
56-
{
57-
self.args
58-
.extend(args.into_iter().map(|s| s.as_ref().to_os_string()));
45+
pub fn arg(mut self, arg: impl AsRef<OsStr>) -> Self {
46+
self.inner.arg(arg);
5947
self
6048
}
6149

6250
/// Build executable `universal-sierra-compiler` command.
6351
#[must_use]
64-
pub fn command(&self) -> Command {
65-
let universal_sierra_compiler = UniversalSierraCompilerCommand::binary_path();
66-
67-
let mut cmd = Command::new(universal_sierra_compiler);
68-
69-
cmd.args(&self.args);
70-
71-
if let Some(path) = &self.current_dir {
72-
cmd.current_dir(path);
73-
}
74-
75-
if self.inherit_stderr {
76-
cmd.stderr(Stdio::inherit());
77-
}
78-
79-
cmd
52+
pub fn command(self) -> Command {
53+
self.inner
8054
}
8155

82-
fn binary_path() -> PathBuf {
83-
env::var("UNIVERSAL_SIERRA_COMPILER")
84-
.map(PathBuf::from)
85-
.ok()
86-
.unwrap_or_else(|| PathBuf::from("universal-sierra-compiler"))
56+
pub fn run(self) -> Result<Output, USCError> {
57+
self.command().output_checked().map_err(USCError::RunFailed)
8758
}
8859
}
60+
61+
/// Ensures that `universal-sierra-compiler` binary is available in the system.
62+
pub fn ensure_available() -> Result<(), USCError> {
63+
which::which(binary_path())
64+
.map(|_| ())
65+
.map_err(USCError::NotFound)
66+
}
67+
68+
/// Returns the binary path either from env or fallback to default name.
69+
fn binary_path() -> String {
70+
env::var("UNIVERSAL_SIERRA_COMPILER")
71+
.unwrap_or_else(|_| "universal-sierra-compiler".to_string())
72+
}

crates/universal-sierra-compiler-api/src/lib.rs

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1-
use anyhow::{Context, Result, anyhow};
1+
use crate::command::USCInternalCommand;
2+
use anyhow::{Result, anyhow};
23
use cairo_lang_casm::hints::Hint;
34
use camino::Utf8Path;
45
use num_bigint::BigInt;
56
use serde::{Deserialize, Serialize};
67
use serde_json::Value;
7-
use std::fmt::Display;
88
use std::io::Write;
9-
use std::str::from_utf8;
9+
use std::process::Command;
10+
use strum_macros::Display;
1011
use tempfile::Builder;
1112

12-
pub use command::*;
13-
use shared::command::CommandExt;
14-
1513
mod command;
1614

1715
pub type CasmCodeOffset = usize;
@@ -71,38 +69,22 @@ pub fn compile_sierra_at_path<T: UniversalSierraCompilerOutput>(
7169
sierra_file_path: &Utf8Path,
7270
sierra_type: &SierraType,
7371
) -> Result<T> {
74-
let usc_output = UniversalSierraCompilerCommand::new()
75-
.inherit_stderr()
76-
.args(vec![
77-
&("compile-".to_string() + &sierra_type.to_string()),
78-
"--sierra-path",
79-
sierra_file_path.as_str(),
80-
])
81-
.command()
82-
.output_checked()
83-
.context(
84-
"Error while compiling Sierra. \
85-
Make sure you have the latest universal-sierra-compiler binary installed. \
86-
Contact us if it doesn't help",
87-
)?;
72+
let usc_output = USCInternalCommand::new()?
73+
.arg(format!("compile-{sierra_type}"))
74+
.arg("--sierra-path")
75+
.arg(sierra_file_path.as_str())
76+
.run()?;
8877

89-
Ok(T::convert(from_utf8(&usc_output.stdout)?.to_string()))
78+
Ok(T::convert(String::from_utf8(usc_output.stdout)?))
9079
}
9180

81+
#[derive(Debug, Display)]
82+
#[strum(serialize_all = "lowercase")]
9283
pub enum SierraType {
9384
Contract,
9485
Raw,
9586
}
9687

97-
impl Display for SierraType {
98-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99-
write!(
100-
f,
101-
"{}",
102-
match self {
103-
SierraType::Contract => "contract",
104-
SierraType::Raw => "raw",
105-
}
106-
)
107-
}
88+
pub fn version_command() -> Result<Command> {
89+
Ok(USCInternalCommand::new()?.arg("--version").command())
10890
}

0 commit comments

Comments
 (0)