impl leo build for multiple aleo files

This commit is contained in:
collin 2022-07-14 18:08:06 -07:00
parent 749a7682cd
commit a6b93cf00d
12 changed files with 159 additions and 107 deletions

1
Cargo.lock generated
View File

@ -1222,6 +1222,7 @@ dependencies = [
"leo-compiler", "leo-compiler",
"leo-errors", "leo-errors",
"leo-package", "leo-package",
"leo-parser",
"leo-span", "leo-span",
"rand", "rand",
"rand_core", "rand_core",

View File

@ -28,6 +28,7 @@ path = "leo/main.rs"
[workspace] [workspace]
members = [ members = [
"compiler/compiler", "compiler/compiler",
"compiler/parser",
"docs/grammar", "docs/grammar",
"errors", "errors",
"leo/package", "leo/package",
@ -46,6 +47,10 @@ version = "1.5.3"
path = "./leo/package" path = "./leo/package"
version = "1.5.3" version = "1.5.3"
[dependencies.leo-parser]
path = "./compiler/parser"
version = "1.5.3"
[dependencies.leo-span] [dependencies.leo-span]
path = "./compiler/span" path = "./compiler/span"
version = "1.5.3" version = "1.5.3"

View File

@ -580,4 +580,11 @@ create_messages!(
msg: format!("i/o operation failed, file: {}, error: {}", file, error), msg: format!("i/o operation failed, file: {}, error: {}", file, error),
help: None, help: None,
} }
@backtraced
failed_to_get_file_name {
args: (),
msg: "Failed to get names of Leo files in the `src/` directory.".to_string(),
help: Some("Check your `src/` directory for invalid file names.".to_string()),
}
); );

View File

@ -0,0 +1,7 @@
program helloworld.aleo;
function main:
input r0 as u32.public;
input r1 as u32.private;
add r0 r1 into r2;
output r2 as u32.private;

View File

@ -0,0 +1,4 @@
program other.aleo;
interface other:
a as u64;

View File

@ -7,7 +7,7 @@ record token:
function main: function main:
input r0 as address.private; input r0 as address.private;
input r1 as u64.private; input r1 as u64.private;
cast r0 0u64 r1 into r2 as token.record; token r0 0u64 r1 into r2;
output r2 as token.record; output r2 as token.record;
function transfer: function transfer:
@ -15,7 +15,7 @@ function transfer:
input r1 as address.private; input r1 as address.private;
input r2 as u64.private; input r2 as u64.private;
sub r0.amount r2 into r3; sub r0.amount r2 into r3;
cast r1 0u64 r2 into r4 as token.record; token r1 0u64 r2 into r4;
cast r0.owner r0.balance r3 into r5 as token.record; token r0.owner r0.balance r3 into r5;
output r4 as token.record; output r4 as token.record;
output r5 as token.record; output r5 as token.record;

View File

@ -14,21 +14,22 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>. // along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::{commands::Command, context::Context}; use crate::{commands::Command, context::Context};
use leo_compiler::{Compiler, InputAst, OutputOptions}; use leo_compiler::{Compiler, InputAst, OutputOptions};
use leo_errors::{CliError, Result}; use leo_errors::{CliError, CompilerError, PackageError, Result};
use leo_package::source::{SourceDirectory, MAIN_FILENAME};
use leo_package::{ use leo_package::{
inputs::InputFile, inputs::InputFile,
outputs::{ChecksumFile, OutputsDirectory, OUTPUTS_DIRECTORY_NAME}, outputs::{ChecksumFile, OutputsDirectory, OUTPUTS_DIRECTORY_NAME},
source::{MainFile, MAIN_FILENAME, SOURCE_DIRECTORY_NAME},
}; };
use leo_span::symbol::with_session_globals;
use aleo::commands::Build as AleoBuild; use aleo::commands::Build as AleoBuild;
use clap::StructOpt; use clap::StructOpt;
use colored::Colorize; use colored::Colorize;
use std::io::Write; use std::io::Write;
use tracing::span::Span; use tracing::span::Span;
/// Compiler Options wrapper for Build command. Also used by other commands which /// Compiler Options wrapper for Build command. Also used by other commands which
@ -91,7 +92,7 @@ impl Command for Build {
let path = context.dir()?; let path = context.dir()?;
// Get the program name. // Get the program name.
let program_name = context.program_name()?; let package_name = context.package_name()?;
// Sanitize the package path to the root directory. // Sanitize the package path to the root directory.
let mut package_path = path.clone(); let mut package_path = path.clone();
@ -103,105 +104,124 @@ impl Command for Build {
let mut output_directory = package_path.clone(); let mut output_directory = package_path.clone();
output_directory.push(OUTPUTS_DIRECTORY_NAME); output_directory.push(OUTPUTS_DIRECTORY_NAME);
tracing::info!("Starting...");
// Compile the main.leo file along with constraints
if !MainFile::exists_at(&package_path) {
return Err(CliError::package_main_file_not_found().into());
}
// Construct the path to the main file in the source directory
let mut main_file_path = package_path.clone();
main_file_path.push(SOURCE_DIRECTORY_NAME);
main_file_path.push(MAIN_FILENAME);
// Load the input file at `package_name.in`
let input_path = InputFile::new(&program_name).setup_file_path(&path);
// Create the outputs directory // Create the outputs directory
OutputsDirectory::create(&package_path)?; OutputsDirectory::create(&package_path)?;
// Log compilation of files to console tracing::info!("Starting...");
tracing::info!("Compiling '{}' in (in \"{}\")", MAIN_FILENAME, main_file_path.display());
// Initialize error handler // Initialize error handler
let handler = leo_errors::emitter::Handler::default(); let handler = leo_errors::emitter::Handler::default();
// Create a new instance of the Leo compiler. // Fetch paths to all .leo files in the source directory.
let mut program = Compiler::new( let files = SourceDirectory::files(&package_path)?;
program_name.to_string(),
String::from("aleo"),
&handler,
main_file_path,
output_directory,
Some(self.compiler_options.into()),
);
program.parse_input(input_path.to_path_buf())?;
// Compute the current program checksum // Compile all `.leo` files into `.aleo` files.
let program_checksum = program.checksum()?; for file_path in files.into_iter().filter(|path| path.is_file()) {
// Construct the Leo file name with extension.
let file_name = file_path
.file_name()
.and_then(|name| name.to_str())
.ok_or_else(PackageError::failed_to_get_file_name)?;
// Compile the program. // Construct program name from file_path name.
{ let program_name = file_name
// Compile the Leo program into Aleo instructions. .strip_suffix(".leo")
let (_, instructions) = program.compile_and_generate_instructions()?; .ok_or_else(PackageError::failed_to_get_file_name)?;
// // Parse the generated Aleo instructions into an Aleo file. // Construct program id header for aleo file.
// let file = AleoFile::<Network>::from_str(&instructions).map_err(CliError::failed_to_load_instructions)?; // Do not create a program with main.aleo as the ID.
// let program_id_name = if file_name.eq(MAIN_FILENAME) {
// // Create the path to the Aleo file. &package_name
// let mut aleo_file_path = package_path.clone(); } else {
// aleo_file_path.push(AleoFile::<Network >::main_file_name()); program_name
// };
// // Write the Aleo file to `main.aleo`.
// file.write_to(&aleo_file_path)
// .map_err(|err| CliError::failed_to_write_to_aleo_file(aleo_file_path.display(), err))?;
// Create the path to the main Aleo file. // Create a new instance of the Leo compiler.
let mut aleo_file_path = package_path.clone(); let mut program = Compiler::new(
aleo_file_path.push(AleoFile::<Network>::main_file_name()); program_id_name.to_string(),
String::from("aleo"), // todo: fetch this from Network::Testnet3
// Write the instructions. &handler,
std::fs::File::create(&aleo_file_path) file_path.clone(),
.map_err(CliError::failed_to_load_instructions)? output_directory.clone(),
.write_all(instructions.as_bytes()) Some(self.compiler_options.clone().into()),
.map_err(CliError::failed_to_load_instructions)?;
// Prepare the path string.
let path_string = format!("(in \"{}\")", aleo_file_path.display());
// Log the build as successful.
tracing::info!(
"✅ Compiled '{}' into Aleo instructions {}",
MAIN_FILENAME,
path_string.dimmed()
); );
// Call the `aleo build` command from the Aleo SDK. // Check if we need to compile the Leo program.
let res = AleoBuild.parse().map_err(CliError::failed_to_execute_aleo_build)?; let checksum_differs = {
// Log the result of the build // Compute the current program checksum.
tracing::info!("{}", res); let program_checksum = program.checksum()?;
// Get the current program checksum.
let checksum_file = ChecksumFile::new(program_name);
// If a checksum file exists, check if it differs from the new checksum.
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?;
program_checksum != previous_checksum
} else {
// By default, the checksum differs if there is no checksum to compare against.
true
};
// If checksum differs, compile the program
if checksum_differs {
// Write the new checksum to the output directory
checksum_file.write_to(&path, program_checksum)?;
tracing::debug!("Checksum saved ({:?})", path);
}
checksum_differs
};
// Compile the program.
if checksum_differs {
// Compile the Leo program into Aleo instructions.
let (_, instructions) = program.compile_and_generate_instructions()?;
// Create the path to the main Aleo file.
let mut aleo_file_path = package_path.clone();
aleo_file_path.push(format!("{}.aleo", program_name));
// Write the instructions.
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)?;
// Prepare the path string.
let path_string = format!("(in \"{}\")", aleo_file_path.display());
// Log the build as successful.
tracing::info!(
"✅ Compiled '{}' into Aleo instructions {}",
file_name,
path_string.dimmed()
);
}
} }
// If a checksum file exists, check if it differs from the new checksum // Call the `aleo build` command from the Aleo SDK.
let checksum_file = ChecksumFile::new(&program_name); let result = AleoBuild.parse().map_err(CliError::failed_to_execute_aleo_build)?;
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?; // Log the result of the build
program_checksum != previous_checksum tracing::info!("{}", result);
// Load the input file at `package_name.in`
let input_file_path = InputFile::new(&package_name).setup_file_path(&path);
// Initialize error handler.
let input_ast = if input_file_path.exists() {
// Load the input file into the source map.
let input_sf = with_session_globals(|s| s.source_map.load_file(&input_file_path))
.map_err(|e| CompilerError::file_read_error(&input_file_path, e))?;
leo_parser::parse_input(&handler, &input_sf.src, input_sf.start_pos).ok()
} else { } else {
// By default, the checksum differs if there is no checksum to compare against todo!() // throw no input files found error.
true
}; };
// If checksum differs, compile the program
if checksum_differs {
// Write the new checksum to the output directory
checksum_file.write_to(&path, program_checksum)?;
tracing::debug!("Checksum saved ({:?})", path);
}
tracing::info!("Complete"); tracing::info!("Complete");
Ok(program.input_ast) Ok(input_ast)
} }
} }

View File

@ -46,7 +46,7 @@ impl Command for Clean {
// Removes the outputs directory. // Removes the outputs directory.
let path_string = OutputsDirectory::remove(&path)?; let path_string = OutputsDirectory::remove(&path)?;
tracing::info!("✅ Cleaned the build directory {}", path_string.dimmed()); tracing::info!("✅ Cleaned the outputs directory {}", path_string.dimmed());
// Call the `aleo clean` command. // Call the `aleo clean` command.
let result = AleoClean.parse().map_err(CliError::failed_to_execute_aleo_clean)?; let result = AleoClean.parse().map_err(CliError::failed_to_execute_aleo_clean)?;

View File

@ -33,8 +33,6 @@ pub use run::Run;
use crate::context::*; use crate::context::*;
use leo_errors::Result; use leo_errors::Result;
use snarkvm::file::AleoFile;
use std::time::Instant; use std::time::Instant;
use tracing::span::Span; use tracing::span::Span;

View File

@ -41,8 +41,8 @@ impl Context {
} }
} }
/// Returns the program name as a String. /// Returns the package name as a String.
pub fn program_name(&self) -> Result<String> { pub fn package_name(&self) -> Result<String> {
// Open the manifest file. // Open the manifest file.
let path = self.dir()?; let path = self.dir()?;
let manifest = Manifest::<Network>::open(&path).map_err(CliError::failed_to_open_manifest)?; let manifest = Manifest::<Network>::open(&path).map_err(CliError::failed_to_open_manifest)?;

View File

@ -42,7 +42,7 @@ impl OutputsDirectory {
} }
if path.exists() { if path.exists() {
fs::remove_dir_all(&path).map_err(PackageError::failed_to_create_inputs_directory)?; fs::remove_dir_all(&path).map_err(|e| PackageError::failed_to_remove_directory(path.display(), e))?;
} }
Ok(format!("(in \"{}\")", path.display())) Ok(format!("(in \"{}\")", path.display()))

View File

@ -16,6 +16,7 @@
use leo_errors::{PackageError, Result}; use leo_errors::{PackageError, Result};
use std::fs::ReadDir;
use std::{ use std::{
borrow::Cow, borrow::Cow,
fs, fs,
@ -45,21 +46,30 @@ impl SourceDirectory {
let mut path = Cow::from(path); let mut path = Cow::from(path);
path.to_mut().push(SOURCE_DIRECTORY_NAME); path.to_mut().push(SOURCE_DIRECTORY_NAME);
let directory = fs::read_dir(&path).map_err(PackageError::failed_to_read_inputs_directory)?; let directory = fs::read_dir(&path).map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
let mut file_paths = Vec::new(); let mut file_paths = Vec::new();
for file_entry in directory {
let file_entry = file_entry.map_err(PackageError::failed_to_get_source_file_entry)?;
let file_path = file_entry.path();
// Verify that the entry is structured as a valid file parse_file_paths(directory, &mut file_paths)?;
let file_type = file_entry
.file_type()
.map_err(|e| PackageError::failed_to_get_source_file_type(file_path.as_os_str().to_owned(), e))?;
if !file_type.is_file() {
return Err(PackageError::invalid_source_file_type(file_path.as_os_str().to_owned(), file_type).into());
}
println!("{:?}", file_paths);
Ok(file_paths)
}
}
fn parse_file_paths(directory: ReadDir, file_paths: &mut Vec<PathBuf>) -> Result<()> {
for file_entry in directory {
let file_entry = file_entry.map_err(PackageError::failed_to_get_source_file_entry)?;
let file_path = file_entry.path();
// Verify that the entry is structured as a valid file or directory
if file_path.is_dir() {
let directory =
fs::read_dir(&file_path).map_err(|err| PackageError::failed_to_read_file(file_path.display(), err))?;
parse_file_paths(directory, file_paths)?;
continue;
} else {
// Verify that the file has the default file extension // Verify that the file has the default file extension
let file_extension = file_path let file_extension = file_path
.extension() .extension()
@ -74,7 +84,7 @@ impl SourceDirectory {
file_paths.push(file_path); file_paths.push(file_path);
} }
Ok(file_paths)
} }
Ok(())
} }