Skip to content

Commit b391198

Browse files
feat(apollo_infra_utils): better cairo0 compiler checks (#5921)
1 parent 96a51f3 commit b391198

File tree

5 files changed

+95
-36
lines changed

5 files changed

+95
-36
lines changed

Cargo.lock

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

crates/apollo_infra_utils/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ num_enum.workspace = true
2020
serde = { workspace = true, features = ["derive"] }
2121
serde_json.workspace = true
2222
socket2 = { workspace = true, optional = true }
23+
thiserror.workspace = true
2324
tokio = { workspace = true, features = ["process", "rt", "time"] }
2425
toml = { workspace = true, optional = true }
2526
tracing.workspace = true
Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
use std::fs;
21
use std::path::PathBuf;
32
use std::process::Command;
43
use std::sync::LazyLock;
54

65
use crate::path::resolve_project_relative_path;
76

7+
#[cfg(test)]
8+
#[path = "cairo0_compiler_test.rs"]
9+
pub mod test;
10+
11+
pub const STARKNET_COMPILE_DEPRECATED: &str = "starknet-compile-deprecated";
12+
pub const CAIRO0_COMPILE: &str = "cairo-compile";
13+
pub const EXPECTED_CAIRO0_VERSION: &str = "0.14.0a1";
14+
815
/// The local python requirements used to determine the cairo0 compiler version.
9-
static PIP_REQUIREMENTS_FILE: LazyLock<PathBuf> =
16+
pub(crate) static PIP_REQUIREMENTS_FILE: LazyLock<PathBuf> =
1017
LazyLock::new(|| resolve_project_relative_path("scripts/requirements.txt").unwrap());
1118

1219
static ENTER_VENV_INSTRUCTIONS: LazyLock<String> = LazyLock::new(|| {
@@ -19,40 +26,67 @@ pip install -r {:#?}"#,
1926
)
2027
});
2128

29+
#[derive(thiserror::Error, Debug)]
30+
pub enum Cairo0CompilerVersionError {
31+
#[error(
32+
"{compiler} version is not correct: required {required}, got {existing}. Are you in the \
33+
venv? If not, run the following commands:\n{}", *ENTER_VENV_INSTRUCTIONS
34+
)]
35+
IncorrectVersion { compiler: String, existing: String, required: String },
36+
#[error(
37+
"{0}. Are you in the venv? If not, run the following commands:\n{}",
38+
*ENTER_VENV_INSTRUCTIONS
39+
)]
40+
NotFound(String),
41+
}
42+
43+
pub fn cairo0_compilers_correct_version() -> Result<(), Cairo0CompilerVersionError> {
44+
for compiler in [CAIRO0_COMPILE, STARKNET_COMPILE_DEPRECATED] {
45+
let version = match Command::new(compiler).arg("--version").output() {
46+
Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
47+
Err(error) => {
48+
return Err(Cairo0CompilerVersionError::NotFound(format!(
49+
"Failed to get {compiler} version: {error}."
50+
)));
51+
}
52+
};
53+
if version
54+
.trim()
55+
.replace("==", " ")
56+
.split(" ")
57+
.nth(1)
58+
.ok_or(Cairo0CompilerVersionError::NotFound("No compiler version found.".to_string()))?
59+
!= EXPECTED_CAIRO0_VERSION
60+
{
61+
return Err(Cairo0CompilerVersionError::IncorrectVersion {
62+
compiler: compiler.to_string(),
63+
existing: version,
64+
required: EXPECTED_CAIRO0_VERSION.to_string(),
65+
});
66+
}
67+
}
68+
69+
Ok(())
70+
}
71+
2272
/// Verifies that the required Cairo0 compiler is available; panics if unavailable.
73+
/// For use in tests only. If cairo0 compiler verification is required in business logic, use
74+
/// `crate::cairo0_compiler::cairo0_compilers_correct_version` instead.
75+
#[cfg(any(test, feature = "testing"))]
2376
pub fn verify_cairo0_compiler_deps() {
24-
// Python compiler. Verify correct version.
25-
let cairo_lang_version_output =
26-
Command::new("sh").arg("-c").arg("pip freeze | grep cairo-lang").output().unwrap().stdout;
27-
let cairo_lang_version_untrimmed = String::from_utf8(cairo_lang_version_output).unwrap();
28-
let cairo_lang_version =
29-
cairo_lang_version_untrimmed.trim().split("==").nth(1).unwrap_or_else(|| {
30-
panic!(
31-
"Unexpected cairo-lang version format '{cairo_lang_version_untrimmed}'. Are you \
32-
in a venv? If not, run:\n{}",
33-
*ENTER_VENV_INSTRUCTIONS
34-
)
35-
});
36-
let requirements_contents = fs::read_to_string(&*PIP_REQUIREMENTS_FILE).unwrap();
37-
let expected_cairo_lang_version = requirements_contents
38-
.lines()
39-
.find(|line| line.starts_with("cairo-lang"))
40-
.unwrap_or_else(|| panic!("Could not find cairo-lang in {:?}.", *PIP_REQUIREMENTS_FILE))
41-
.trim()
42-
.split("==")
43-
.nth(1)
44-
.unwrap_or_else(|| {
45-
panic!(
46-
"Malformed cairo-lang dependency (expected 'cairo-lang==X') in {:?}.",
47-
*PIP_REQUIREMENTS_FILE
48-
)
49-
});
50-
51-
assert_eq!(
52-
expected_cairo_lang_version, cairo_lang_version,
53-
"cairo-lang version {expected_cairo_lang_version} not found (installed version: \
54-
{cairo_lang_version}). Run the following commands (enter a python venv and install \
55-
dependencies) and retry:\n{}",
77+
let specific_error = match cairo0_compilers_correct_version() {
78+
Ok(_) => {
79+
return;
80+
}
81+
Err(Cairo0CompilerVersionError::NotFound(_)) => "no installed cairo-lang found".to_string(),
82+
Err(Cairo0CompilerVersionError::IncorrectVersion { existing, .. }) => {
83+
format!("installed version: {existing}")
84+
}
85+
};
86+
87+
panic!(
88+
"cairo-lang version {EXPECTED_CAIRO0_VERSION} not found ({specific_error}). Please enter \
89+
a venv and rerun the test:\n{}",
5690
*ENTER_VENV_INSTRUCTIONS
5791
);
5892
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use crate::cairo0_compiler::{EXPECTED_CAIRO0_VERSION, PIP_REQUIREMENTS_FILE};
2+
3+
#[test]
4+
fn test_cairo0_version_pip_requirements() {
5+
let requirements_contents = std::fs::read_to_string(&*PIP_REQUIREMENTS_FILE).unwrap();
6+
let pip_cairo_lang_version = requirements_contents
7+
.lines()
8+
.find(|line| line.starts_with("cairo-lang"))
9+
.unwrap_or_else(|| panic!("Could not find cairo-lang in {:?}.", *PIP_REQUIREMENTS_FILE))
10+
.trim()
11+
.split("==")
12+
.nth(1)
13+
.unwrap_or_else(|| {
14+
panic!(
15+
"Malformed cairo-lang dependency (expected 'cairo-lang==X') in {:?}.",
16+
*PIP_REQUIREMENTS_FILE
17+
)
18+
});
19+
assert_eq!(pip_cairo_lang_version, format!("{EXPECTED_CAIRO0_VERSION}"));
20+
}

crates/blockifier_test_utils/src/cairo_compile.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use std::io::Write;
22
use std::path::PathBuf;
33
use std::process::{Command, Output, Stdio};
44

5-
use apollo_infra_utils::cairo0_compiler::verify_cairo0_compiler_deps;
5+
use apollo_infra_utils::cairo0_compiler::{
6+
verify_cairo0_compiler_deps,
7+
STARKNET_COMPILE_DEPRECATED,
8+
};
69
use apollo_infra_utils::cairo_compiler_version::cairo1_compiler_version;
710
use apollo_infra_utils::path::project_path;
811
use tempfile::NamedTempFile;
@@ -92,7 +95,7 @@ pub fn cairo0_compile(
9295
debug_info: bool,
9396
) -> CompilationArtifacts {
9497
verify_cairo0_compiler_deps();
95-
let mut command = Command::new("starknet-compile-deprecated");
98+
let mut command = Command::new(STARKNET_COMPILE_DEPRECATED);
9699
command.arg(&path);
97100
if let Some(extra_arg) = extra_arg {
98101
command.arg(extra_arg);

0 commit comments

Comments
 (0)