mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-12-24 10:41:57 +03:00
impl leo build for multiple aleo files
This commit is contained in:
parent
749a7682cd
commit
a6b93cf00d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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"
|
||||||
|
@ -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()),
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
7
examples/helloworld/main.aleo
Normal file
7
examples/helloworld/main.aleo
Normal 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;
|
4
examples/helloworld/other.aleo
Normal file
4
examples/helloworld/other.aleo
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
program other.aleo;
|
||||||
|
interface other:
|
||||||
|
a as u64;
|
||||||
|
|
@ -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;
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)?;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)?;
|
||||||
|
@ -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()))
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user