Skip to content

Commit

Permalink
feat: executable command
Browse files Browse the repository at this point in the history
commit-id:8d24c938
  • Loading branch information
FroyaTheHen committed Dec 18, 2024
1 parent 34ba7f5 commit 9bf96a7
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 0 deletions.
19 changes: 19 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 @@ -17,6 +17,7 @@ members = [
"utils/scarb-ui",
"utils/test-for-each-example",
"xtask",
"extensions/scarb-cairo-execute",
]
"resolver" = "2"

Expand Down Expand Up @@ -70,6 +71,7 @@ cairo-lang-test-plugin = "*"
cairo-lang-test-runner = "*"
cairo-lang-utils = { version = "*", features = ["env_logger"] }
cairo-language-server = "*"
cairo-vm = "*"
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"
version.workspace = true
edition.workspace = true
authors.workspace = true
homepage.workspace = true
license.workspace = true
readme.workspace = true
repository.workspace = true
publish = false


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


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

#[derive(Parser, Debug)]
#[command(author, version, about = "Execute a Scarb-built Cairo package.")]
struct Args {
/// Name of the package.
#[command(flatten)]
packages_filter: PackagesFilter,

/// Do not rebuild the package (use existing build artifacts).
#[arg(long, default_value_t = false)]
no_build: bool,
}

fn main() -> ExitCode {
let args = Args::parse();
let ui = Ui::new(Default::default(), OutputFormat::Text);

// Retrieve workspace metadata.
let metadata = match MetadataCommand::new().inherit_stderr().exec() {
Ok(metadata) => metadata,
Err(error) => {
eprintln!("Failed to retrieve metadata: {error:?}");
return ExitCode::FAILURE;
}
};

// Load the targeted package metadata.
let package = match args.packages_filter.match_one(&metadata) {
Ok(package) => package,
Err(error) => {
eprintln!("Failed to find a matching package in the workspace: {error:?}");
return ExitCode::FAILURE;
}
};

// Build the package if `--no-build` is not passed.
if !args.no_build {
ui.print(format!("Building package: {}", package.name));
if let Err(error) = ScarbCommand::new().arg("build").run() {
eprintln!("Failed to build package: {error:?}");
return ExitCode::FAILURE;
}
}

// Locate the executable file.
let executable_path = find_executable(&package).unwrap();

ui.print(format!("Found executable: {}", executable_path));

// Run the executable with any provided arguments.
run_executable(&executable_path)
}

/// Find the executable for the given package.
fn find_executable(package: &PackageMetadata) -> Result<Utf8PathBuf> {
let build_dir =
Utf8PathBuf::from(std::env::var("SCARB_TARGET_DIR").unwrap_or_else(|_| "target".into()));
let profile = std::env::var("SCARB_PROFILE").unwrap_or_else(|_| "debug".into()); // Use "debug" by default.
let executable_name = &package.name; // The executable name often matches the package name.

let executable_path = build_dir.join(profile).join("bin").join(executable_name);
ensure!(
executable_path.exists(),
format!(
"Executable `{}` not found. Did you forget to build the package?",
executable_path
)
);
Ok(executable_path)
}

/// Run the executable.
fn run_executable(executable_path: &Utf8PathBuf) -> ExitCode {
let file = File::open(executable_path).unwrap();
let executable: Executable = serde_json::from_reader(file).unwrap();

// Convert the executable path into a valid string.
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::NonReturning)) // ?? Bootloader 2.9.2 TODO: fixme
.unwrap();
Program::new(
entrypoint.builtins.clone(),
data,
Some(entrypoint.offset),
hints,
Default::default(),
Default::default(),
vec![],
None,
)
.unwrap()
};

let mut hint_processor = CairoHintProcessor {
runner: None,
user_args: vec![vec![]], // is relevant??
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::default();

// Execute the Cairo program.
if let Err(_err) = cairo_run_program(&program, &cairo_run_config, &mut hint_processor) {
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}
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")
.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 9bf96a7

Please sign in to comment.