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",
"dirs",
"from-pest",
"indexmap",
"lazy_static",
"leo-ast",
"leo-compiler",

View File

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

View File

@ -21,6 +21,7 @@ use anyhow::{anyhow, Result};
use std::{
fs::{create_dir_all, File},
io::{Read, Write},
path::PathBuf,
};
use structopt::StructOpt;
use tracing::Span;
@ -80,7 +81,7 @@ impl Add {
impl Command for Add {
type Input = ();
type Output = ();
type Output = PathBuf;
fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Adding")
@ -98,6 +99,8 @@ impl Command for Add {
let (author, package_name) = self.try_read_arguments()?;
tracing::info!("Package: {}/{}", &author, &package_name);
// Attempt to fetch the package.
let reader = {
let fetch = Fetch {
@ -118,9 +121,9 @@ impl Command for Add {
// 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()));
path.push(format!("{}-{}@{}", author.clone(), package_name, self.version.unwrap()));
} else {
path.push(package_name);
path.push(package_name.clone());
}
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 indexmap::set::IndexSet;
use structopt::StructOpt;
use tracing::span::Span;
@ -29,31 +30,65 @@ use tracing::span::Span;
pub struct Install {}
impl Command for Install {
type Input = ();
/// Names of dependencies in the current branch of a dependency tree.
type Input = IndexSet<String>;
type Output = ();
fn log_span(&self) -> Span {
tracing::span!(tracing::Level::INFO, "Installing")
}
fn prelude(&self, _: Context) -> Result<Self::Input> {
Ok(())
fn prelude(&self, context: Context) -> Result<Self::Input> {
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> {
let deps = context
.manifest()?
.get_package_dependencies()
.ok_or_else(|| anyhow!("Package has no dependencies"))?;
fn apply(self, context: Context, mut tree: Self::Input) -> Result<Self::Output> {
let dependencies = context
.manifest()
.map_err(|_| anyhow!("Package Manifest not found"))?
.get_package_dependencies();
for (_name, dep) in deps.iter() {
Add::new(
let dependencies = match dependencies {
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,
Some(dep.author.clone()),
Some(dep.package.clone()),
Some(dep.version.clone()),
Some(dependency.author.clone()),
Some(package_name.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(())

View File

@ -247,6 +247,7 @@ mod cli_tests {
use crate::{run_with_args, Opt};
use anyhow::Error;
use snarkvm_utilities::Write;
use std::path::PathBuf;
use structopt::StructOpt;
use test_dir::{DirBuilder, FileType, TestDir};
@ -366,6 +367,7 @@ mod cli_tests {
}
#[test]
#[ignore]
fn test_import() {
let dir = testdir("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/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.
fn is_included(path: &Path) -> bool {
// excluded directories: `output`, `imports`
if path.ends_with(OUTPUTS_DIRECTORY_NAME.trim_end_matches('/'))
| path.ends_with(IMPORTS_DIRECTORY_NAME.trim_end_matches('/'))
{
// DO NOT include `imports` and `outputs` directories.
if path.starts_with(IMPORTS_DIRECTORY_NAME) || path.starts_with(OUTPUTS_DIRECTORY_NAME) {
return false;
}