Skip to content

Commit b9541ed

Browse files
feat(apollo_infra_utils): better cairo0 compiler checks
1 parent 7243c38 commit b9541ed

File tree

5 files changed

+97
-34
lines changed

5 files changed

+97
-34
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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@ 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
2627

2728
[dev-dependencies]
29+
assert-json-diff.workspace = true
2830
cached.workspace = true
31+
colored.workspace = true
2932
nix.workspace = true
3033
pretty_assertions.workspace = true
3134
rstest.workspace = true
Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,81 @@
1-
use std::fs;
1+
#[cfg(any(test, feature = "testing"))]
22
use std::path::PathBuf;
33
use std::process::Command;
4+
#[cfg(any(test, feature = "testing"))]
45
use std::sync::LazyLock;
56

7+
#[cfg(any(test, feature = "testing"))]
68
use crate::path::resolve_project_relative_path;
79

10+
#[cfg(test)]
11+
#[path = "cairo0_compiler_test.rs"]
12+
pub mod test;
13+
14+
pub const STARKNET_COMPILE_DEPRECATED: &str = "starknet-compile-deprecated";
15+
pub const CAIRO0_COMPILE: &str = "cairo-compile";
16+
pub const EXPECTED_CAIRO0_VERSION: &str = "0.14.0a1";
17+
818
/// The local python requirements used to determine the cairo0 compiler version.
9-
static PIP_REQUIREMENTS_FILE: LazyLock<PathBuf> =
19+
#[cfg(any(test, feature = "testing"))]
20+
pub(crate) static PIP_REQUIREMENTS_FILE: LazyLock<PathBuf> =
1021
LazyLock::new(|| resolve_project_relative_path("scripts/requirements.txt").unwrap());
1122

23+
#[derive(thiserror::Error, Debug)]
24+
pub enum Cairo0CompilerVersionError {
25+
#[error("{compiler} version is not correct: required {required}, got {existing}.")]
26+
IncorrectVersion { compiler: String, existing: String, required: String },
27+
#[error("{0} not found.")]
28+
NotFound(String),
29+
}
30+
31+
pub fn cairo0_compilers_correct_version() -> Result<(), Cairo0CompilerVersionError> {
32+
for compiler in [CAIRO0_COMPILE, STARKNET_COMPILE_DEPRECATED] {
33+
let version = match Command::new(compiler).arg("--version").output() {
34+
Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
35+
Err(error) => {
36+
return Err(Cairo0CompilerVersionError::NotFound(format!(
37+
"Failed to get {compiler} version: {error}."
38+
)));
39+
}
40+
};
41+
if version
42+
.trim()
43+
.replace("==", " ")
44+
.split(" ")
45+
.nth(1)
46+
.ok_or(Cairo0CompilerVersionError::NotFound("No compiler version found.".to_string()))?
47+
!= EXPECTED_CAIRO0_VERSION
48+
{
49+
return Err(Cairo0CompilerVersionError::IncorrectVersion {
50+
compiler: compiler.to_string(),
51+
existing: version,
52+
required: EXPECTED_CAIRO0_VERSION.to_string(),
53+
});
54+
}
55+
}
56+
57+
Ok(())
58+
}
59+
1260
/// Verifies that the required Cairo0 compiler is available; panics if unavailable.
61+
/// For use in tests only. If cairo0 compiler verification is required in business logic, use
62+
/// `crate::cairo0_compiler::cairo0_compilers_correct_version` instead.
63+
#[cfg(any(test, feature = "testing"))]
1364
pub fn verify_cairo0_compiler_deps() {
14-
// Python compiler. Verify correct version.
15-
let cairo_lang_version_output =
16-
Command::new("sh").arg("-c").arg("pip freeze | grep cairo-lang").output().unwrap().stdout;
17-
let cairo_lang_version_untrimmed = String::from_utf8(cairo_lang_version_output).unwrap();
18-
let cairo_lang_version =
19-
cairo_lang_version_untrimmed.trim().split("==").nth(1).unwrap_or_else(|| {
20-
panic!("Unexpected cairo-lang version format '{cairo_lang_version_untrimmed}'.")
21-
});
22-
let requirements_contents = fs::read_to_string(&*PIP_REQUIREMENTS_FILE).unwrap();
23-
let expected_cairo_lang_version = requirements_contents
24-
.lines()
25-
.find(|line| line.starts_with("cairo-lang"))
26-
.unwrap_or_else(|| panic!("Could not find cairo-lang in {:?}.", *PIP_REQUIREMENTS_FILE))
27-
.trim()
28-
.split("==")
29-
.nth(1)
30-
.unwrap_or_else(|| {
31-
panic!(
32-
"Malformed cairo-lang dependency (expected 'cairo-lang==X') in {:?}.",
33-
*PIP_REQUIREMENTS_FILE
34-
)
35-
});
36-
37-
assert_eq!(
38-
expected_cairo_lang_version, cairo_lang_version,
39-
"cairo-lang version {expected_cairo_lang_version} not found (installed version: \
40-
{cairo_lang_version}). Run the following commands (enter a python venv and install \
41-
dependencies) and retry:\npython3.9 -m venv sequencer_venv\n. \
42-
sequencer_venv/bin/activate\npip install -r {:?}",
43-
*PIP_REQUIREMENTS_FILE
65+
let specific_error = match cairo0_compilers_correct_version() {
66+
Ok(_) => {
67+
return;
68+
}
69+
Err(Cairo0CompilerVersionError::NotFound(_)) => "no installed cairo-lang found".to_string(),
70+
Err(Cairo0CompilerVersionError::IncorrectVersion { existing, .. }) => {
71+
format!("installed version: {existing}")
72+
}
73+
};
74+
75+
panic!(
76+
"cairo-lang version {EXPECTED_CAIRO0_VERSION} not found ({specific_error}). Run the \
77+
following commands (enter a python venv and install dependencies) and retry:\npython -m \
78+
venv sequencer_venv\n. sequencer_venv/bin/activate\npip install -r {:?}",
79+
PIP_REQUIREMENTS_FILE.to_str().expect("Path to requirements.txt is valid unicode.")
4480
);
4581
}
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)