diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 82de5ab734..1649be02b2 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -28,6 +28,7 @@ use crate::{ pub use leo_asg::{new_context, AsgContext as Context, AsgContext}; use leo_asg::{Asg, AsgPass, FormattedError, Program as AsgProgram}; use leo_ast::{Input, MainInput, Program as AstProgram}; +use leo_imports::ImportParser; use leo_input::LeoInputParser; use leo_package::inputs::InputPairs; use leo_parser::parse_ast; @@ -39,6 +40,7 @@ use snarkvm_r1cs::{ConstraintSynthesizer, ConstraintSystem, SynthesisError}; use sha2::{Digest, Sha256}; use std::{ + collections::HashMap, fs, marker::PhantomData, path::{Path, PathBuf}, @@ -68,6 +70,7 @@ pub struct Compiler<'a, F: PrimeField, G: GroupType> { asg: Option>, options: CompilerOptions, proof_options: TheoremOptions, + imports_map: HashMap, _engine: PhantomData, _group: PhantomData, } @@ -83,6 +86,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { context: AsgContext<'a>, options: Option, proof_options: Option, + imports_map: HashMap, ) -> Self { Self { program_name: package_name.clone(), @@ -94,6 +98,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { context, options: options.unwrap_or_default(), proof_options: proof_options.unwrap_or_default(), + imports_map, _engine: PhantomData, _group: PhantomData, } @@ -113,6 +118,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { context: AsgContext<'a>, options: Option, proof_options: Option, + imports_map: HashMap, ) -> Result { let mut compiler = Self::new( package_name, @@ -121,6 +127,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { context, options, proof_options, + imports_map, ); compiler.parse_program()?; @@ -152,6 +159,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { context: AsgContext<'a>, options: Option, proof_options: Option, + imports_map: HashMap, ) -> Result { let mut compiler = Self::new( package_name, @@ -160,6 +168,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { context, options, proof_options, + imports_map, ); compiler.parse_input(input_string, input_path, state_string, state_path)?; @@ -263,7 +272,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { let asg = Asg::new( self.context, &self.program, - &mut leo_imports::ImportParser::new(self.main_file_path.clone()), + &mut ImportParser::new(self.main_file_path.clone(), self.imports_map.clone()), )?; if self.proof_options.type_inferenced { diff --git a/compiler/tests/mod.rs b/compiler/tests/mod.rs index b488ea9f00..cf83a42034 100644 --- a/compiler/tests/mod.rs +++ b/compiler/tests/mod.rs @@ -33,7 +33,7 @@ use leo_compiler::{ use snarkvm_curves::edwards_bls12::Fq; use snarkvm_r1cs::TestConstraintSystem; -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; pub const TEST_OUTPUT_DIRECTORY: &str = "/output/"; const EMPTY_FILE: &str = ""; @@ -52,7 +52,15 @@ fn new_compiler() -> EdwardsTestCompiler { let path = PathBuf::from("/test/src/main.leo"); let output_dir = PathBuf::from(TEST_OUTPUT_DIRECTORY); - EdwardsTestCompiler::new(program_name, path, output_dir, make_test_context(), None, None) + EdwardsTestCompiler::new( + program_name, + path, + output_dir, + make_test_context(), + None, + None, + HashMap::new(), + ) } pub(crate) fn parse_program(program_string: &str) -> Result { diff --git a/compiler/tests/test.rs b/compiler/tests/test.rs index d699321ad5..7262412575 100644 --- a/compiler/tests/test.rs +++ b/compiler/tests/test.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use std::path::{Path, PathBuf}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; use leo_asg::*; use leo_synthesizer::{CircuitSynthesizer, SerializedCircuit, SummarizedCircuit}; @@ -40,7 +43,15 @@ fn new_compiler(path: PathBuf) -> EdwardsTestCompiler { let program_name = "test".to_string(); let output_dir = PathBuf::from("/output/"); - EdwardsTestCompiler::new(program_name, path, output_dir, make_test_context(), None, None) + EdwardsTestCompiler::new( + program_name, + path, + output_dir, + make_test_context(), + None, + None, + HashMap::new(), + ) } pub(crate) fn parse_program(program_string: &str) -> Result { diff --git a/imports/src/parser/import_parser.rs b/imports/src/parser/import_parser.rs index c8fcfdf43a..51cf3392dc 100644 --- a/imports/src/parser/import_parser.rs +++ b/imports/src/parser/import_parser.rs @@ -18,7 +18,7 @@ use crate::errors::ImportParserError; use leo_asg::{AsgContext, AsgConvertError, ImportResolver, Program, Span}; use indexmap::{IndexMap, IndexSet}; -use std::path::PathBuf; +use std::{collections::HashMap, path::PathBuf}; /// Stores imported packages. /// @@ -29,14 +29,16 @@ pub struct ImportParser<'a> { program_path: PathBuf, partial_imports: IndexSet, imports: IndexMap>, + pub imports_map: HashMap, } impl<'a> ImportParser<'a> { - pub fn new(program_path: PathBuf) -> Self { + pub fn new(program_path: PathBuf, imports_map: HashMap) -> Self { ImportParser { program_path, partial_imports: Default::default(), imports: Default::default(), + imports_map, } } } @@ -55,9 +57,8 @@ impl<'a> ImportResolver<'a> for ImportParser<'a> { if let Some(program) = self.imports.get(&full_path) { return Ok(Some(program.clone())); } - let mut imports = Self::default(); let path = self.program_path.clone(); - + let mut imports = self.clone(); // Self::default() was previously self.partial_imports.insert(full_path.clone()); let program = imports .parse_package(context, path, package_segments, span) diff --git a/imports/src/parser/parse_package.rs b/imports/src/parser/parse_package.rs index a14f12a2cc..a8b3a65d11 100644 --- a/imports/src/parser/parse_package.rs +++ b/imports/src/parser/parse_package.rs @@ -103,21 +103,30 @@ impl<'a> ImportParser<'a> { .collect::, std::io::Error>>() .map_err(|error| ImportParserError::directory_error(error, span, &error_path))?; + // Keeping backward compatibilty for existing packages. + // If index_map contains key, use it or try to access directly. + // TODO: Remove when migration is possible. + let package_name = self + .imports_map + .get(package_name) + .unwrap_or(&package_name.to_string()) + .clone(); + // Check if the imported package name is in the imports directory. let matched_import_entry = entries .into_iter() - .find(|entry| entry.file_name().into_string().unwrap().eq(package_name)); + .find(|entry| entry.file_name().into_string().unwrap().eq(&package_name)); // Check if the package name was found in both the source and imports directory. match (matched_source_entry, matched_import_entry) { (Some(_), Some(_)) => Err(ImportParserError::conflicting_imports(Identifier::new_with_span( - package_name, + &package_name, span.clone(), ))), (Some(source_entry), None) => self.parse_package_access(context, &source_entry, &segments[1..], span), (None, Some(import_entry)) => self.parse_package_access(context, &import_entry, &segments[1..], span), (None, None) => Err(ImportParserError::unknown_package(Identifier::new_with_span( - package_name, + &package_name, span.clone(), ))), } diff --git a/leo/commands/build.rs b/leo/commands/build.rs index ef701b4d87..ec2326a300 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -34,6 +34,8 @@ use snarkvm_r1cs::ConstraintSystem; use structopt::StructOpt; use tracing::span::Span; +use std::collections::HashMap; + /// Compiler Options wrapper for Build command. Also used by other commands which /// require Build command output as their input. #[derive(StructOpt, Clone, Debug)] @@ -126,7 +128,11 @@ impl Command for Build { fn apply(self, context: Context, _: Self::Input) -> Result { let path = context.dir()?; - let package_name = context.manifest()?.get_package_name(); + let manifest = context + .manifest() + .map_err(|_| anyhow!("Package manifest not found, try running `leo init`"))?; + let package_name = manifest.get_package_name(); + let imports_map = manifest.get_imports_map().unwrap_or(HashMap::new()); // Sanitize the package path to the root directory. let mut package_path = path.clone(); @@ -174,6 +180,7 @@ impl Command for Build { thread_leaked_context(), Some(self.compiler_options.clone().into()), Some(self.compiler_options.into()), + imports_map, )?; // Compute the current program checksum diff --git a/leo/commands/mod.rs b/leo/commands/mod.rs index 0616a3aaea..7f5f2349fb 100644 --- a/leo/commands/mod.rs +++ b/leo/commands/mod.rs @@ -33,9 +33,6 @@ pub use deploy::Deploy; pub mod init; pub use init::Init; -pub mod install; -pub use install::Install; - pub mod lint; pub use lint::Lint; diff --git a/leo/commands/package/add.rs b/leo/commands/package/add.rs index 5b540617ba..4d070b7e2c 100644 --- a/leo/commands/package/add.rs +++ b/leo/commands/package/add.rs @@ -101,9 +101,9 @@ impl Command for Add { // Attempt to fetch the package. let reader = { let fetch = Fetch { - author, + author: author.clone(), package_name: package_name.clone(), - version: self.version, + version: self.version.clone(), }; let bytes = context.api.run_route(fetch)?.bytes()?; std::io::Cursor::new(bytes) @@ -114,7 +114,14 @@ impl Command for Add { { ImportsDirectory::create(&path)?; path.push(IMPORTS_DIRECTORY_NAME); - path.push(package_name); + + // Dumb compatibility hack. + // TODO: Remove once `leo add` functionality is discussed. + if self.version.is_some() { + path.push(format!("{}-{}@{}", author, package_name, self.version.unwrap())); + } else { + path.push(package_name); + } create_dir_all(&path)?; }; diff --git a/leo/commands/install.rs b/leo/commands/package/install.rs similarity index 91% rename from leo/commands/install.rs rename to leo/commands/package/install.rs index cf19ba55bd..f1e50cd344 100644 --- a/leo/commands/install.rs +++ b/leo/commands/package/install.rs @@ -14,7 +14,10 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{commands::Command, context::Context}; +use crate::{ + commands::{package::Add, Command}, + context::Context, +}; use anyhow::{anyhow, Result}; use structopt::StructOpt; @@ -43,20 +46,16 @@ impl Command for Install { .get_package_dependencies() .ok_or_else(|| anyhow!("Package has no dependencies"))?; - use crate::commands::package::Add; - for (_name, dep) in deps.iter() { Add::new( None, Some(dep.author.clone()), - Some(dep.name.clone()), + Some(dep.package.clone()), Some(dep.version.clone()), ) .execute(context.clone())?; } - dbg!(deps); - Ok(()) } } diff --git a/leo/commands/package/mod.rs b/leo/commands/package/mod.rs index ce8c637f59..434b9dbd47 100644 --- a/leo/commands/package/mod.rs +++ b/leo/commands/package/mod.rs @@ -20,6 +20,9 @@ pub use add::Add; pub mod clone; pub use clone::Clone; +pub mod install; +pub use install::Install; + pub mod login; pub use login::Login; diff --git a/leo/commands/test.rs b/leo/commands/test.rs index 3de2c52715..6b97e737b2 100644 --- a/leo/commands/test.rs +++ b/leo/commands/test.rs @@ -113,6 +113,7 @@ impl Command for Test { thread_leaked_context(), Some(self.compiler_options.clone().into()), Some(self.compiler_options.clone().into()), + std::collections::HashMap::new(), )?; let temporary_program = program; diff --git a/leo/main.rs b/leo/main.rs index 88711724ef..062ca3b9c6 100644 --- a/leo/main.rs +++ b/leo/main.rs @@ -22,13 +22,12 @@ pub mod logger; pub mod updater; use commands::{ - package::{Add, Clone, Login, Logout, Publish, Remove}, + package::{Add, Clone, Install, Login, Logout, Publish, Remove}, Build, Clean, Command, Deploy, Init, - Install, Lint, New, Prove, diff --git a/package/src/root/manifest.rs b/package/src/root/manifest.rs index 9e998a7547..017eb5bc4f 100644 --- a/package/src/root/manifest.rs +++ b/package/src/root/manifest.rs @@ -38,7 +38,7 @@ pub struct Remote { pub struct Dependency { pub author: String, pub version: String, - pub name: String, + pub package: String, } #[derive(Deserialize)] @@ -85,6 +85,23 @@ impl Manifest { self.dependencies.clone() } + /// Get HashMap of kind: + /// import name => import directory + /// Which then used in AST/ASG to resolve import paths. + pub fn get_imports_map(&self) -> Option> { + self.dependencies.clone().map(|dependencies| { + dependencies + .into_iter() + .map(|(name, dependency)| { + ( + name, + format!("{}-{}@{}", dependency.author, dependency.package, dependency.version), + ) + }) + .collect() + }) + } + pub fn get_package_license(&self) -> Option { self.project.license.clone() } @@ -119,6 +136,10 @@ license = "MIT" [remote] author = "{author}" # Add your Aleo Package Manager username or team name. + +[dependencies] +# Define dependencies here in format: +# name = {{ package = "package-name", author = "author", version = "version" }} "#, name = self.project.name, author = author