cairo_lang_executable/
compile.rs1use std::path::Path;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5use cairo_lang_compiler::db::RootDatabase;
6use cairo_lang_compiler::diagnostics::DiagnosticsReporter;
7use cairo_lang_compiler::project::setup_project;
8use cairo_lang_filesystem::ids::CrateId;
9use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
10use cairo_lang_runnable_utils::builder::{
11 CasmProgramWrapperInfo, EntryCodeConfig, RunnableBuilder,
12};
13use cairo_lang_sierra_generator::db::SierraGenGroup;
14use cairo_lang_sierra_generator::executables::find_executable_function_ids;
15use cairo_lang_sierra_generator::program_generator::SierraProgramWithDebug;
16use cairo_lang_sierra_to_casm::compiler::CairoProgram;
17use cairo_lang_utils::{Upcast, write_comma_separated};
18use itertools::Itertools;
19
20use crate::plugin::{EXECUTABLE_PREFIX, EXECUTABLE_RAW_ATTR, executable_plugin_suite};
21
22pub struct CompiledFunction {
24 pub program: CairoProgram,
26 pub wrapper: CasmProgramWrapperInfo,
28}
29impl std::fmt::Display for CompiledFunction {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "# builtins:")?;
32 if !self.wrapper.builtins.is_empty() {
33 write!(f, " ")?;
34 write_comma_separated(f, self.wrapper.builtins.iter().map(|b| b.to_str()))?;
35 }
36 writeln!(f)?;
37 writeln!(f, "# header #")?;
38 for instruction in &self.wrapper.header {
39 writeln!(f, "{};", instruction)?;
40 }
41 writeln!(f, "# sierra based code #")?;
42 write!(f, "{}", self.program)?;
43 writeln!(f, "# footer #")?;
44 for instruction in &self.wrapper.footer {
45 writeln!(f, "{};", instruction)?;
46 }
47 Ok(())
48 }
49}
50
51pub fn compile_executable(
54 path: &Path,
55 executable_path: Option<&str>,
56 diagnostics_reporter: DiagnosticsReporter<'_>,
57) -> Result<CompiledFunction> {
58 let mut db = RootDatabase::builder()
59 .skip_auto_withdraw_gas()
60 .detect_corelib()
61 .with_plugin_suite(executable_plugin_suite())
62 .build()?;
63
64 let main_crate_ids = setup_project(&mut db, Path::new(&path))?;
65
66 compile_executable_in_prepared_db(&db, executable_path, main_crate_ids, diagnostics_reporter)
67}
68
69pub fn compile_executable_in_prepared_db(
73 db: &RootDatabase,
74 executable_path: Option<&str>,
75 main_crate_ids: Vec<CrateId>,
76 mut diagnostics_reporter: DiagnosticsReporter<'_>,
77) -> Result<CompiledFunction> {
78 let mut executables: Vec<_> = find_executable_function_ids(db, main_crate_ids)
79 .into_iter()
80 .filter_map(|(id, labels)| {
81 labels.into_iter().any(|l| l == EXECUTABLE_RAW_ATTR).then_some(id)
82 })
83 .collect();
84
85 if let Some(executable_path) = executable_path {
86 executables
87 .retain(|executable| originating_function_path(db, *executable) == executable_path);
88 };
89 let executable = match executables.len() {
90 0 => {
91 diagnostics_reporter.ensure(db)?;
93 anyhow::bail!("Requested `#[executable]` not found.");
94 }
95 1 => executables[0],
96 _ => {
97 let executable_names = executables
98 .iter()
99 .map(|executable| originating_function_path(db, *executable))
100 .join("\n ");
101 anyhow::bail!(
102 "More than one executable found in the main crate: \n {}\nUse --executable to \
103 specify which to compile.",
104 executable_names
105 );
106 }
107 };
108
109 compile_executable_function_in_prepared_db(db, executable, diagnostics_reporter)
110}
111
112fn originating_function_path(db: &RootDatabase, wrapper: ConcreteFunctionWithBodyId) -> String {
116 let wrapper_name = wrapper.name(db);
117 let wrapper_full_path = wrapper.base_semantic_function(db).full_path(db.upcast());
118 let Some(wrapped_name) = wrapper_name.strip_prefix(EXECUTABLE_PREFIX) else {
119 return wrapper_full_path;
120 };
121 let Some(wrapper_path_to_module) = wrapper_full_path.strip_suffix(wrapper_name.as_str()) else {
122 return wrapper_full_path;
123 };
124 format!("{}{}", wrapper_path_to_module, wrapped_name)
125}
126
127pub fn compile_executable_function_in_prepared_db(
137 db: &RootDatabase,
138 executable: ConcreteFunctionWithBodyId,
139 mut diagnostics_reporter: DiagnosticsReporter<'_>,
140) -> Result<CompiledFunction> {
141 diagnostics_reporter.ensure(db)?;
142 let SierraProgramWithDebug { program: sierra_program, debug_info: _ } = Arc::unwrap_or_clone(
143 db.get_sierra_program_for_functions(vec![executable])
144 .ok()
145 .with_context(|| "Compilation failed without any diagnostics.")?,
146 );
147 let executable_func = sierra_program.funcs[0].clone();
148 let builder = RunnableBuilder::new(sierra_program, None)?;
149 let wrapper = builder.create_wrapper_info(&executable_func, EntryCodeConfig::executable())?;
150 Ok(CompiledFunction { program: builder.casm_program().clone(), wrapper })
151}