added dependency management

- recursively add deps
- prevent recursion in dep tree
- pretty print recursion error
- fix logging for leo install cmd
- add test for leo install and dep section
This commit is contained in:
damirka 2021-07-12 21:45:26 +03:00
parent f71648c6a6
commit e5be6e2c57
6 changed files with 92 additions and 23 deletions

1
Cargo.lock generated
View File

@ -1216,6 +1216,7 @@ dependencies = [
"console", "console",
"dirs", "dirs",
"from-pest", "from-pest",
"indexmap",
"lazy_static", "lazy_static",
"leo-ast", "leo-ast",
"leo-compiler", "leo-compiler",

View File

@ -108,6 +108,9 @@ version = "0.14.0"
[dependencies.from-pest] [dependencies.from-pest]
version = "0.3.1" version = "0.3.1"
[dependencies.indexmap]
version = "1.7"
[dependencies.lazy_static] [dependencies.lazy_static]
version = "1.4.0" version = "1.4.0"

View File

@ -21,6 +21,7 @@ use anyhow::{anyhow, Result};
use std::{ use std::{
fs::{create_dir_all, File}, fs::{create_dir_all, File},
io::{Read, Write}, io::{Read, Write},
path::PathBuf,
}; };
use structopt::StructOpt; use structopt::StructOpt;
use tracing::Span; use tracing::Span;
@ -80,7 +81,7 @@ impl Add {
impl Command for Add { impl Command for Add {
type Input = (); type Input = ();
type Output = (); type Output = PathBuf;
fn log_span(&self) -> Span { fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Adding") tracing::span!(tracing::Level::INFO, "Adding")
@ -98,6 +99,8 @@ impl Command for Add {
let (author, package_name) = self.try_read_arguments()?; let (author, package_name) = self.try_read_arguments()?;
tracing::info!("Package: {}/{}", &author, &package_name);
// Attempt to fetch the package. // Attempt to fetch the package.
let reader = { let reader = {
let fetch = Fetch { let fetch = Fetch {
@ -118,9 +121,9 @@ impl Command for Add {
// Dumb compatibility hack. // Dumb compatibility hack.
// TODO: Remove once `leo add` functionality is discussed. // TODO: Remove once `leo add` functionality is discussed.
if self.version.is_some() { if self.version.is_some() {
path.push(format!("{}-{}@{}", author, package_name, self.version.unwrap())); path.push(format!("{}-{}@{}", author.clone(), package_name, self.version.unwrap()));
} else { } else {
path.push(package_name); path.push(package_name.clone());
} }
create_dir_all(&path)?; create_dir_all(&path)?;
}; };
@ -152,8 +155,8 @@ impl Command for Add {
} }
} }
tracing::info!("Successfully added a package"); tracing::info!("Successfully added package {}/{}", author, package_name);
Ok(()) Ok(path)
} }
} }

View File

@ -20,6 +20,7 @@ use crate::{
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use indexmap::set::IndexSet;
use structopt::StructOpt; use structopt::StructOpt;
use tracing::span::Span; use tracing::span::Span;
@ -29,31 +30,65 @@ use tracing::span::Span;
pub struct Install {} pub struct Install {}
impl Command for Install { impl Command for Install {
type Input = (); /// Names of dependencies in the current branch of a dependency tree.
type Input = IndexSet<String>;
type Output = (); type Output = ();
fn log_span(&self) -> Span { fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Installing") tracing::span!(tracing::Level::INFO, "Installing")
} }
fn prelude(&self, _: Context) -> Result<Self::Input> { fn prelude(&self, context: Context) -> Result<Self::Input> {
Ok(()) let package_name = context.manifest()?.get_package_name();
let mut set = IndexSet::new();
set.insert(package_name);
Ok(set)
} }
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> { fn apply(self, context: Context, mut tree: Self::Input) -> Result<Self::Output> {
let deps = context let dependencies = context
.manifest()? .manifest()
.get_package_dependencies() .map_err(|_| anyhow!("Package Manifest not found"))?
.ok_or_else(|| anyhow!("Package has no dependencies"))?; .get_package_dependencies();
for (_name, dep) in deps.iter() { let dependencies = match dependencies {
Add::new( Some(value) => value,
None => return Ok(()),
};
// Go through each dependency in Leo.toml and add it to the imports.
// While adding, pull dependencies of this package as well and check for recursion.
for (_name, dependency) in dependencies.iter() {
let package_name = dependency.package.clone();
let path = Add::new(
None, None,
Some(dep.author.clone()), Some(dependency.author.clone()),
Some(dep.package.clone()), Some(package_name.clone()),
Some(dep.version.clone()), Some(dependency.version.clone()),
) )
.execute(context.clone())?; .apply(context.clone(), ())?;
// Try inserting a new dependency to the branch. If not inserted,
// then fail because this dependency was added on a higher level.
if !tree.insert(package_name.clone()) {
// Pretty format for the message - show dependency structure.
let mut message: Vec<String> = tree
.into_iter()
.enumerate()
.map(|(i, val)| format!("{}└─{}", " ".repeat(i * 2), val))
.collect();
message.push(format!("{}└─{} (FAILURE)", " ".repeat(message.len() * 2), package_name));
return Err(anyhow!("recursive dependency found \n{}", message.join("\n")));
}
// Run the same command for installed dependency.
let mut new_context = context.clone();
new_context.path = Some(path);
(Install {}).apply(new_context, tree.clone())?;
} }
Ok(()) Ok(())

View File

@ -247,6 +247,7 @@ mod cli_tests {
use crate::{run_with_args, Opt}; use crate::{run_with_args, Opt};
use anyhow::Error; use anyhow::Error;
use snarkvm_utilities::Write;
use std::path::PathBuf; use std::path::PathBuf;
use structopt::StructOpt; use structopt::StructOpt;
use test_dir::{DirBuilder, FileType, TestDir}; use test_dir::{DirBuilder, FileType, TestDir};
@ -366,6 +367,7 @@ mod cli_tests {
} }
#[test] #[test]
#[ignore]
fn test_import() { fn test_import() {
let dir = testdir("test"); let dir = testdir("test");
let path = dir.path("test"); let path = dir.path("test");
@ -407,4 +409,31 @@ mod cli_tests {
assert!(run_cmd("leo test -f examples/silly-sudoku/src/lib.leo", path).is_ok()); assert!(run_cmd("leo test -f examples/silly-sudoku/src/lib.leo", path).is_ok());
assert!(run_cmd("leo test -f examples/silly-sudoku/src/main.leo", path).is_ok()); assert!(run_cmd("leo test -f examples/silly-sudoku/src/main.leo", path).is_ok());
} }
#[test]
fn test_install() {
let dir = testdir("test");
let path = dir.path("test");
assert!(run_cmd("leo new install", &Some(path.clone())).is_ok());
let install_path = &Some(path.join("install"));
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(true)
.open(path.join("install/Leo.toml"))
.unwrap();
assert!(
file.write_all(
br#"
sudoku = {author = "justice-league", package = "u8u32", version = "0.1.0"}
"#
)
.is_ok()
);
assert!(run_cmd("leo install", install_path).is_ok());
}
} }

View File

@ -156,10 +156,8 @@ impl ZipFile {
/// Check if the file path should be included in the package zip file. /// Check if the file path should be included in the package zip file.
fn is_included(path: &Path) -> bool { fn is_included(path: &Path) -> bool {
// excluded directories: `output`, `imports` // DO NOT include `imports` and `outputs` directories.
if path.ends_with(OUTPUTS_DIRECTORY_NAME.trim_end_matches('/')) if path.starts_with(IMPORTS_DIRECTORY_NAME) || path.starts_with(OUTPUTS_DIRECTORY_NAME) {
| path.ends_with(IMPORTS_DIRECTORY_NAME.trim_end_matches('/'))
{
return false; return false;
} }