Skip to content

Commit

Permalink
feat: executable command
Browse files Browse the repository at this point in the history
commit-id:c985abe1
  • Loading branch information
FroyaTheHen committed Jan 8, 2025
1 parent 3f94464 commit 088284e
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 0 deletions.
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ members = [
"scarb",
"scarb-metadata",
"extensions/scarb-doc",
"extensions/scarb-cairo-execute",
"extensions/scarb-cairo-language-server",
"extensions/scarb-cairo-run",
"extensions/scarb-cairo-test",
Expand Down Expand Up @@ -71,6 +72,7 @@ cairo-lang-test-plugin = "*"
cairo-lang-test-runner = "*"
cairo-lang-utils = { version = "*", features = ["env_logger"] }
cairo-language-server = "*"
cairo-vm = "1.0.1"
camino = { version = "1", features = ["serde1"] }
cargo_metadata = ">=0.18"
clap = { version = "4", features = ["derive", "env", "string"] }
Expand Down
29 changes: 29 additions & 0 deletions extensions/scarb-cairo-execute/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "scarb-cairo-execute"
publish = false

authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
version.workspace = true

[dependencies]
anyhow.workspace = true
cairo-lang-executable.workspace = true
cairo-lang-runner.workspace = true
cairo-vm.workspace = true
camino.workspace = true
clap.workspace = true
indoc.workspace = true
num-bigint.workspace = true
serde.workspace = true
serde_json.workspace = true
scarb-metadata = { path = "../../scarb-metadata" }
scarb-ui = { path = "../../utils/scarb-ui" }

[dev-dependencies]
scarb-test-support = { path = "../../utils/scarb-test-support" }
assert_fs.workspace = true
146 changes: 146 additions & 0 deletions extensions/scarb-cairo-execute/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use anyhow::{ensure, Result};
use cairo_lang_executable::executable::{EntryPointKind, Executable};
use cairo_lang_runner::{build_hints_dict, Arg, CairoHintProcessor};
use cairo_vm::cairo_run::cairo_run_program;
use cairo_vm::cairo_run::CairoRunConfig;
use cairo_vm::types::layout_name::LayoutName;
use cairo_vm::types::program::Program;
use cairo_vm::types::relocatable::MaybeRelocatable;
use cairo_vm::Felt252;
use camino::Utf8PathBuf;
use clap::Parser;
use indoc::formatdoc;
use num_bigint::BigInt;
use scarb_metadata::MetadataCommand;
use scarb_ui::args::{PackagesFilter, VerbositySpec};
use scarb_ui::components::Status;
use scarb_ui::Ui;
use std::env;
use std::process::ExitCode;

/// Compiles a Cairo project and runs a function marked `#[executable]`.
/// Exits with 1 if the compilation or run fails, otherwise 0.
#[derive(Parser, Debug)]
#[clap(version, verbatim_doc_comment)]
struct Args {
/// Name of the package.
#[command(flatten)]
packages_filter: PackagesFilter,

/// Whether to only run a prebuilt executable.
#[arg(long, default_value_t = true)]
prebuilt: bool,

#[clap(flatten)]
run: RunArgs,

/// Logging verbosity.
#[command(flatten)]
pub verbose: VerbositySpec,
}

#[derive(Parser, Debug)]
struct RunArgs {
/// Serialized arguments to the executable function.
#[arg(long, value_delimiter = ',')]
args: Vec<BigInt>,
}

fn main() -> ExitCode {
let args = Args::parse();
let ui = Ui::new(args.verbose.clone().into(), scarb_ui::OutputFormat::Text);

match main_inner(args, ui.clone()) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
ui.error(format!("{error:#}"));
ExitCode::FAILURE
}
}
}

fn main_inner(args: Args, ui: Ui) -> Result<(), anyhow::Error> {
let metadata = MetadataCommand::new().inherit_stderr().exec()?;
let package = args
.packages_filter
.match_one(&metadata)
.map_err(|e| anyhow::anyhow!("Failed to match package in workspace: {e:?}"))?;

let filename = format!("{}.executable.json", package.name);
let path = Utf8PathBuf::from(env::var("SCARB_TARGET_DIR")?).join(env::var("SCARB_PROFILE")?);
ui.print(Status::new("Running", &package.name));
let executable = load_prebuilt_executable(&path, filename)?;

let data = executable
.program
.bytecode
.iter()
.map(Felt252::from)
.map(MaybeRelocatable::from)
.collect();

let (hints, string_to_hint) = build_hints_dict(&executable.program.hints);

let program = {
let entrypoint = executable
.entrypoints
.iter()
.find(|e| matches!(e.kind, EntryPointKind::Bootloader))
.ok_or_else(|| anyhow::anyhow!("Bootloader entrypoint not found"))?;
Program::new(
entrypoint.builtins.clone(),
data,
Some(entrypoint.offset),
hints,
Default::default(),
Default::default(),
vec![],
None,
)
};

let mut hint_processor = CairoHintProcessor {
runner: None,
user_args: vec![vec![Arg::Array(
args.run.args.iter().map(|v| Arg::Value(v.into())).collect(),
)]],
string_to_hint,
starknet_state: Default::default(),
run_resources: Default::default(),
syscalls_used_resources: Default::default(),
no_temporary_segments: false,
};

let cairo_run_config = CairoRunConfig {
trace_enabled: true,
relocate_mem: false,
layout: LayoutName::all_cairo,
proof_mode: false,
secure_run: Some(true),
allow_missing_builtins: Some(true),
..Default::default()
};

cairo_run_program(&program?, &cairo_run_config, &mut hint_processor)
.map_err(|e| anyhow::anyhow!("Cairo program run failed: {e:?}"))?;

Ok(())
}

fn load_prebuilt_executable(
path: &Utf8PathBuf,
filename: String,
) -> Result<Executable, anyhow::Error> {
let file_path = path.join(&filename);
ensure!(
file_path.exists(),
formatdoc! {r#"
package has not been compiled, file does not exist: {filename}
help: run `scarb build` to compile the package
"#}
);
let file = std::fs::File::open(file_path)
.map_err(|e| anyhow::anyhow!("Failed to open file: {}", e))?;
serde_json::from_reader(file)
.map_err(|e| anyhow::anyhow!("Failed parsing prebuilt executable: {}", e))
}
31 changes: 31 additions & 0 deletions extensions/scarb-cairo-execute/tests/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use assert_fs::TempDir;
use indoc::indoc;
use scarb_test_support::command::Scarb;
use scarb_test_support::project_builder::ProjectBuilder;

#[test]
fn can_run_default_main_function_from_executable() {
let t = TempDir::new().unwrap();
ProjectBuilder::start()
.name("hello")
.version("0.1.0")
.dep_cairo_execute()
.manifest_extra(indoc! {r#"
[executable]
"#})
.lib_cairo(indoc! {r#"
#[executable]
fn main() -> felt252 {
42
}
"#})
.build(&t);

Scarb::quick_snapbox().arg("build").current_dir(&t).assert();

Scarb::quick_snapbox()
.arg("cairo-execute")
.current_dir(&t)
.assert()
.success();
}

0 comments on commit 088284e

Please sign in to comment.