use std::path::Path;
use std::time::Instant;
use anyhow::Result;
use console::style;
use crate::config;
use crate::config::schema::YmConfig;
use crate::workspace::graph::WorkspaceGraph;
use crate::workspace::resolver;
pub fn execute() -> Result<()> {
let total_start = Instant::now();
let (config_path, cfg) = config::load_or_find_config()?;
let project = config::project_dir(&config_path);
let (root_project, root_cfg) = resolve_install_root(&project, cfg)?;
if root_cfg.workspaces.is_some() {
install_workspace(&root_project, &root_cfg)?;
} else {
install_single(&root_project, &root_cfg)?;
}
let elapsed = total_start.elapsed();
let time = if elapsed.as_millis() > 1000 {
format!("{:.2}s", elapsed.as_secs_f64())
} else {
format!("{}ms", elapsed.as_millis())
};
println!(
"{} install in {}",
style(format!("{:>12}", "Finished")).green().bold(),
time,
);
Ok(())
}
fn resolve_install_root(project: &Path, cfg: YmConfig) -> Result<(std::path::PathBuf, YmConfig)> {
if cfg.workspaces.is_some() {
return Ok((project.to_path_buf(), cfg));
}
if let Some(ws_root) = config::find_workspace_root(project) {
if ws_root != project {
let root_config_path = ws_root.join(config::CONFIG_FILE);
let root_cfg = config::load_config(&root_config_path)?;
if root_cfg.workspaces.is_some() {
return Ok((ws_root, root_cfg));
}
}
}
Ok((project.to_path_buf(), cfg))
}
fn install_workspace(root: &Path, root_cfg: &YmConfig) -> Result<()> {
eprintln!(" Scanning workspace...");
let ws = WorkspaceGraph::build(root)?;
let mut packages = ws.all_packages();
packages.sort();
if packages.is_empty() {
eprintln!(
"{} no workspace modules found",
style(format!("{:>12}", "warning")).yellow().bold(),
);
return Ok(());
}
eprintln!(" Scanning workspace ({} modules)...", packages.len());
for name in &packages {
let pkg = ws.get_package(name).unwrap();
let errors = pkg.config.validate_workspace_deps(root_cfg);
if !errors.is_empty() {
for e in &errors {
eprintln!(
"{} {}: {}",
style(format!("{:>12}", "error")).red().bold(),
name,
e,
);
}
anyhow::bail!("Invalid workspace dependency declarations in '{}'", name);
}
}
let dep_start = Instant::now();
let own_module_deps: std::collections::HashMap<String, std::collections::BTreeMap<String, String>> = packages
.iter()
.map(|name| {
let pkg = ws.get_package(name).unwrap();
let mut deps = pkg.config.maven_dependencies_with_root(root_cfg);
for (k, v) in root_cfg.resolved_resolutions(root_cfg) {
if deps.contains_key(&k) {
deps.insert(k, v);
}
}
(name.clone(), deps)
})
.collect();
let closure_cache: std::collections::HashMap<String, Vec<String>> = packages
.iter()
.map(|name| (name.clone(), ws.transitive_closure(name).unwrap_or_default()))
.collect();
let all_module_deps: Vec<(String, std::collections::BTreeMap<String, String>)> = packages
.iter()
.map(|name| {
let mut deps = own_module_deps.get(name).cloned().unwrap_or_default();
if let Some(closure) = closure_cache.get(name) {
for ws_dep in closure {
if ws_dep != name {
if let Some(ws_dep_deps) = own_module_deps.get(ws_dep) {
for (k, v) in ws_dep_deps {
deps.entry(k.clone()).or_insert(v.clone());
}
}
}
}
}
(name.clone(), deps)
})
.collect();
let unique_artifacts: usize = {
let mut set = std::collections::BTreeSet::new();
for (_, deps) in &all_module_deps {
set.extend(deps.keys().cloned());
}
set.len()
};
eprintln!(
" Resolving dependencies ({} modules, {} artifacts)...",
packages.len(),
unique_artifacts,
);
let cache = config::maven_cache_dir();
let mut resolved = config::load_lockfile_checked(root, root_cfg)?;
let registries = root_cfg.registry_entries();
let mut exclusions = root_cfg.exclusions.as_ref().cloned().unwrap_or_default();
exclusions.extend(root_cfg.per_dependency_exclusions());
exclusions.extend(root_cfg.resolved_exclusions());
let resolutions = root_cfg.resolved_resolutions(root_cfg);
let _per_module_jars = resolver::resolve_workspace_deps_with_resolutions(
&all_module_deps,
&cache,
&mut resolved,
®istries,
&exclusions,
&resolutions,
)?;
config::save_lockfile(root, &resolved)?;
let dep_time = dep_start.elapsed();
let total_jars = resolved.dependencies.len();
println!(
"{} dependencies ({} jars) {:>25}ms",
style(format!("{:>12}", "Resolving")).green().bold(),
total_jars,
dep_time.as_millis(),
);
let conflicts = resolver::check_conflicts(&resolved);
if !conflicts.is_empty() {
for (ga, versions) in &conflicts {
eprintln!(
"{} version conflict: {} has versions: {}",
style(format!("{:>12}", "warning")).yellow().bold(),
style(ga).bold(),
versions.join(", "),
);
}
eprintln!(" Use [resolutions] in ym.json to pin a specific version");
}
Ok(())
}
fn install_single(project: &Path, cfg: &YmConfig) -> Result<()> {
let dep_start = Instant::now();
let jars = super::build::resolve_and_persist_deps(project, cfg)?;
let dep_time = dep_start.elapsed();
println!(
"{} dependencies ({} jars) {:>25}ms",
style(format!("{:>12}", "Resolving")).green().bold(),
jars.len(),
dep_time.as_millis(),
);
Ok(())
}