leo_lang/cli/commands/
build.rsuse super::*;
use leo_ast::Stub;
use leo_compiler::{Compiler, CompilerOptions, OutputOptions};
use leo_errors::{CliError, UtilError};
use leo_package::{build::BuildDirectory, outputs::OutputsDirectory, source::SourceDirectory};
use leo_retriever::{Manifest, NetworkName, Retriever};
use leo_span::Symbol;
use snarkvm::{
package::Package,
prelude::{MainnetV0, Network, ProgramID, TestnetV0},
};
use indexmap::IndexMap;
use snarkvm::prelude::CanaryV0;
use std::{
io::Write,
path::{Path, PathBuf},
str::FromStr,
};
impl From<BuildOptions> for CompilerOptions {
fn from(options: BuildOptions) -> Self {
let mut out_options = Self {
build: leo_compiler::BuildOptions {
dce_enabled: options.enable_dce,
conditional_block_max_depth: options.conditional_block_max_depth,
disable_conditional_branch_type_checking: options.disable_conditional_branch_type_checking,
},
output: OutputOptions {
ast_spans_enabled: options.enable_ast_spans,
initial_ast: options.enable_initial_ast_snapshot,
unrolled_ast: options.enable_unrolled_ast_snapshot,
ssa_ast: options.enable_ssa_ast_snapshot,
flattened_ast: options.enable_flattened_ast_snapshot,
destructured_ast: options.enable_destructured_ast_snapshot,
inlined_ast: options.enable_inlined_ast_snapshot,
dce_ast: options.enable_dce_ast_snapshot,
},
};
if options.enable_all_ast_snapshots {
out_options.output.initial_ast = true;
out_options.output.unrolled_ast = true;
out_options.output.ssa_ast = true;
out_options.output.flattened_ast = true;
out_options.output.destructured_ast = true;
out_options.output.inlined_ast = true;
out_options.output.dce_ast = true;
}
out_options
}
}
#[derive(Parser, Debug)]
pub struct LeoBuild {
#[clap(flatten)]
pub(crate) options: BuildOptions,
}
impl Command for LeoBuild {
type Input = ();
type Output = ();
fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Leo")
}
fn prelude(&self, _: Context) -> Result<Self::Input> {
Ok(())
}
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
let network = NetworkName::try_from(context.get_network(&self.options.network)?)?;
match network {
NetworkName::MainnetV0 => handle_build::<MainnetV0>(&self, context),
NetworkName::TestnetV0 => handle_build::<TestnetV0>(&self, context),
NetworkName::CanaryV0 => handle_build::<CanaryV0>(&self, context),
}
}
}
fn handle_build<N: Network>(command: &LeoBuild, context: Context) -> Result<<LeoBuild as Command>::Output> {
let package_path = context.dir()?;
let home_path = context.home()?;
let manifest = Manifest::read_from_dir(&package_path)?;
let program_id = ProgramID::<N>::from_str(manifest.program())?;
let build_directory = package_path.join("build");
if build_directory.exists() {
std::fs::remove_dir_all(&build_directory).map_err(CliError::build_error)?;
}
Package::create(&build_directory, &program_id).map_err(CliError::build_error)?;
let handler = Handler::default();
let main_sym = Symbol::intern(&program_id.name().to_string());
let mut retriever = Retriever::<N>::new(
main_sym,
&package_path,
&home_path,
context.get_endpoint(&command.options.endpoint)?.to_string(),
)
.map_err(|err| UtilError::failed_to_retrieve_dependencies(err, Default::default()))?;
let mut local_dependencies =
retriever.retrieve().map_err(|err| UtilError::failed_to_retrieve_dependencies(err, Default::default()))?;
local_dependencies.push(main_sym);
let recursive_build = !command.options.non_recursive;
for dependency in local_dependencies.into_iter() {
if recursive_build || dependency == main_sym {
let (local_path, stubs) = retriever.prepare_local(dependency)?;
let local_outputs_directory = OutputsDirectory::create(&local_path)?;
let local_build_directory = BuildDirectory::create(&local_path)?;
let local_source_files = SourceDirectory::files(&local_path)?;
SourceDirectory::check_files(&local_source_files)?;
for file_path in local_source_files {
compile_leo_file(
file_path,
&ProgramID::<N>::try_from(format!("{}.aleo", dependency))
.map_err(|_| UtilError::snarkvm_error_building_program_id(Default::default()))?,
&local_outputs_directory,
&local_build_directory,
&handler,
command.options.clone(),
stubs.clone(),
)?;
}
}
retriever.process_local(dependency, recursive_build)?;
}
Package::<N>::open(&build_directory).map_err(CliError::failed_to_execute_build)?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn compile_leo_file<N: Network>(
file_path: PathBuf,
program_id: &ProgramID<N>,
outputs: &Path,
build: &Path,
handler: &Handler,
options: BuildOptions,
stubs: IndexMap<Symbol, Stub>,
) -> Result<()> {
let program_name = program_id.name().to_string();
let mut aleo_file_path = build.to_path_buf();
aleo_file_path.push(format!("main.{}", program_id.network()));
let mut compiler = Compiler::<N>::new(
program_name.clone(),
program_id.network().to_string(),
handler,
file_path.clone(),
outputs.to_path_buf(),
Some(options.into()),
stubs,
);
let instructions = compiler.compile()?;
std::fs::File::create(&aleo_file_path)
.map_err(CliError::failed_to_load_instructions)?
.write_all(instructions.as_bytes())
.map_err(CliError::failed_to_load_instructions)?;
tracing::info!("✅ Compiled '{program_name}.aleo' into Aleo instructions");
Ok(())
}