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-errors",
"leo-package",
"leo-parser",
"leo-span",
"rand",
"rand_core",

View File

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

View File

@ -580,4 +580,11 @@ create_messages!(
msg: format!("i/o operation failed, file: {}, error: {}", file, error),
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:
input r0 as address.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;
function transfer:
@ -15,7 +15,7 @@ function transfer:
input r1 as address.private;
input r2 as u64.private;
sub r0.amount r2 into r3;
cast r1 0u64 r2 into r4 as token.record;
cast r0.owner r0.balance r3 into r5 as token.record;
token r1 0u64 r2 into r4;
token r0.owner r0.balance r3 into r5;
output r4 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
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::{commands::Command, context::Context};
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::{
inputs::InputFile,
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 clap::StructOpt;
use colored::Colorize;
use std::io::Write;
use tracing::span::Span;
/// Compiler Options wrapper for Build command. Also used by other commands which
@ -91,7 +92,7 @@ impl Command for Build {
let path = context.dir()?;
// Get the program name.
let program_name = context.program_name()?;
let package_name = context.package_name()?;
// Sanitize the package path to the root directory.
let mut package_path = path.clone();
@ -103,105 +104,124 @@ impl Command for Build {
let mut output_directory = package_path.clone();
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
OutputsDirectory::create(&package_path)?;
// Log compilation of files to console
tracing::info!("Compiling '{}' in (in \"{}\")", MAIN_FILENAME, main_file_path.display());
tracing::info!("Starting...");
// Initialize error handler
let handler = leo_errors::emitter::Handler::default();
// Create a new instance of the Leo compiler.
let mut program = Compiler::new(
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())?;
// Fetch paths to all .leo files in the source directory.
let files = SourceDirectory::files(&package_path)?;
// Compute the current program checksum
let program_checksum = program.checksum()?;
// Compile all `.leo` files into `.aleo` files.
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.
{
// Compile the Leo program into Aleo instructions.
let (_, instructions) = program.compile_and_generate_instructions()?;
// Construct program name from file_path name.
let program_name = file_name
.strip_suffix(".leo")
.ok_or_else(PackageError::failed_to_get_file_name)?;
// // Parse the generated Aleo instructions into an Aleo file.
// let file = AleoFile::<Network>::from_str(&instructions).map_err(CliError::failed_to_load_instructions)?;
//
// // Create the path to the Aleo file.
// let mut aleo_file_path = package_path.clone();
// aleo_file_path.push(AleoFile::<Network >::main_file_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))?;
// Construct program id header for aleo file.
// Do not create a program with main.aleo as the ID.
let program_id_name = if file_name.eq(MAIN_FILENAME) {
&package_name
} else {
program_name
};
// Create the path to the main Aleo file.
let mut aleo_file_path = package_path.clone();
aleo_file_path.push(AleoFile::<Network>::main_file_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 {}",
MAIN_FILENAME,
path_string.dimmed()
// Create a new instance of the Leo compiler.
let mut program = Compiler::new(
program_id_name.to_string(),
String::from("aleo"), // todo: fetch this from Network::Testnet3
&handler,
file_path.clone(),
output_directory.clone(),
Some(self.compiler_options.clone().into()),
);
// Call the `aleo build` command from the Aleo SDK.
let res = AleoBuild.parse().map_err(CliError::failed_to_execute_aleo_build)?;
// Log the result of the build
tracing::info!("{}", res);
// Check if we need to compile the Leo program.
let checksum_differs = {
// Compute the current program checksum.
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
let checksum_file = ChecksumFile::new(&program_name);
let checksum_differs = if checksum_file.exists_at(&package_path) {
let previous_checksum = checksum_file.read_from(&package_path)?;
program_checksum != previous_checksum
// Call the `aleo build` command from the Aleo SDK.
let result = AleoBuild.parse().map_err(CliError::failed_to_execute_aleo_build)?;
// Log the result of the build
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 {
// By default, the checksum differs if there is no checksum to compare against
true
todo!() // throw no input files found error.
};
// 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");
Ok(program.input_ast)
Ok(input_ast)
}
}

View File

@ -46,7 +46,7 @@ impl Command for Clean {
// Removes the outputs directory.
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.
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 leo_errors::Result;
use snarkvm::file::AleoFile;
use std::time::Instant;
use tracing::span::Span;

View File

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

View File

@ -42,7 +42,7 @@ impl OutputsDirectory {
}
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()))

View File

@ -16,6 +16,7 @@
use leo_errors::{PackageError, Result};
use std::fs::ReadDir;
use std::{
borrow::Cow,
fs,
@ -45,21 +46,30 @@ impl SourceDirectory {
let mut path = Cow::from(path);
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();
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
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());
}
parse_file_paths(directory, &mut file_paths)?;
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
let file_extension = file_path
.extension()
@ -74,7 +84,7 @@ impl SourceDirectory {
file_paths.push(file_path);
}
Ok(file_paths)
}
Ok(())
}