leo_lang/cli/commands/
build.rs1use super::*;
18
19use leo_ast::Stub;
20use leo_compiler::{AstSnapshots, Compiler, CompilerOptions};
21use leo_errors::{CliError, UtilError};
22use leo_package::{Manifest, NetworkName, Package};
23use leo_span::Symbol;
24
25use snarkvm::prelude::{MainnetV0, Network, TestnetV0};
26
27use indexmap::IndexMap;
28use snarkvm::prelude::CanaryV0;
29use std::path::Path;
30
31impl From<BuildOptions> for CompilerOptions {
32 fn from(options: BuildOptions) -> Self {
33 Self {
34 ast_spans_enabled: options.enable_ast_spans,
35 ast_snapshots: if options.enable_all_ast_snapshots {
36 AstSnapshots::All
37 } else {
38 AstSnapshots::Some(options.ast_snapshots.into_iter().collect())
39 },
40 initial_ast: options.enable_all_ast_snapshots | options.enable_initial_ast_snapshot,
41 }
42 }
43}
44
45#[derive(Parser, Debug)]
47pub struct LeoBuild {
48 #[clap(flatten)]
49 pub(crate) options: BuildOptions,
50 #[clap(flatten)]
51 pub(crate) env_override: EnvOptions,
52}
53
54impl Command for LeoBuild {
55 type Input = ();
56 type Output = Package;
57
58 fn log_span(&self) -> Span {
59 tracing::span!(tracing::Level::INFO, "Leo")
60 }
61
62 fn prelude(&self, _: Context) -> Result<Self::Input> {
63 Ok(())
64 }
65
66 fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
67 let network: NetworkName = context.get_network(&self.env_override.network)?.parse()?;
69 match network {
70 NetworkName::MainnetV0 => handle_build::<MainnetV0>(&self, context),
71 NetworkName::TestnetV0 => handle_build::<TestnetV0>(&self, context),
72 NetworkName::CanaryV0 => handle_build::<CanaryV0>(&self, context),
73 }
74 }
75}
76
77fn handle_build<N: Network>(command: &LeoBuild, context: Context) -> Result<<LeoBuild as Command>::Output> {
79 let package_path = context.dir()?;
80 let home_path = context.home()?;
81
82 let package = if command.options.build_tests {
83 leo_package::Package::from_directory_with_tests(&package_path, &home_path, command.options.no_cache)?
84 } else {
85 leo_package::Package::from_directory(&package_path, &home_path, command.options.no_cache)?
86 };
87
88 let outputs_directory = package.outputs_directory();
89 let build_directory = package.build_directory();
90 let imports_directory = package.imports_directory();
91 let source_directory = package.source_directory();
92 let main_source_path = source_directory.join("main.leo");
93
94 for dir in [&outputs_directory, &build_directory, &imports_directory] {
95 std::fs::create_dir_all(dir).map_err(|err| {
96 UtilError::util_file_io_error(format_args!("Couldn't create directory {}", dir.display()), err)
97 })?;
98 }
99
100 let handler = Handler::default();
102
103 let mut stubs: IndexMap<Symbol, Stub> = IndexMap::new();
104
105 for program in package.programs.iter() {
106 let (bytecode, build_path) = match &program.data {
107 leo_package::ProgramData::Bytecode(bytecode) => {
108 (bytecode.clone(), imports_directory.join(format!("{}.aleo", program.name)))
110 }
111 leo_package::ProgramData::SourcePath(path) => {
112 let build_path = if path == &main_source_path {
114 build_directory.join("main.aleo")
115 } else {
116 imports_directory.join(format!("{}.aleo", program.name))
117 };
118 let bytecode = compile_leo_file::<N>(
119 path,
120 program.name,
121 program.is_test,
122 &outputs_directory,
123 &handler,
124 command.options.clone(),
125 stubs.clone(),
126 )?;
127 (bytecode, build_path)
128 }
129 };
130
131 std::fs::write(build_path, &bytecode).map_err(CliError::failed_to_load_instructions)?;
133
134 let stub = leo_disassembler::disassemble_from_str::<N>(program.name, &bytecode)?;
136 stubs.insert(program.name, stub);
137 }
138
139 let build_manifest_path = build_directory.join(leo_package::MANIFEST_FILENAME);
142 let fake_manifest = Manifest {
143 program: package.manifest.program.clone(),
144 version: "0.1.0".to_string(),
145 description: String::new(),
146 license: String::new(),
147 dependencies: None,
148 dev_dependencies: None,
149 };
150 fake_manifest.write_to_file(build_manifest_path)?;
151
152 Ok(package)
153}
154
155#[allow(clippy::too_many_arguments)]
157fn compile_leo_file<N: Network>(
158 source_file_path: &Path,
159 program_name: Symbol,
160 is_test: bool,
161 output_path: &Path,
162 handler: &Handler,
163 options: BuildOptions,
164 stubs: IndexMap<Symbol, Stub>,
165) -> Result<String> {
166 let mut compiler = Compiler::<N>::new(
168 Some(program_name.to_string()),
169 is_test,
170 handler.clone(),
171 output_path.to_path_buf(),
172 Some(options.into()),
173 stubs,
174 );
175
176 let bytecode = compiler.compile_from_file(source_file_path)?;
178
179 tracing::info!(" {} statements before dead code elimination.", compiler.statements_before_dce);
180 tracing::info!(" {} statements after dead code elimination.", compiler.statements_after_dce);
181
182 tracing::info!("✅ Compiled '{program_name}.aleo' into Aleo instructions");
183 Ok(bytecode)
184}