Files
uv/crates/puffin-dev/src/resolve_cli.rs
T

115 lines
3.4 KiB
Rust
Raw Normal View History

use std::io::{BufWriter, Write};
use std::path::PathBuf;
2023-11-01 16:46:37 +01:00
use anstream::println;
use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
2023-11-17 19:26:55 +01:00
use clap::{Parser, ValueEnum};
2023-12-18 09:42:58 -05:00
use fs_err::File;
2023-11-01 16:54:47 +01:00
use itertools::Itertools;
use petgraph::dot::{Config as DotConfig, Dot};
2023-11-01 16:46:37 +01:00
use pep508_rs::Requirement;
use platform_host::Platform;
use puffin_cache::{Cache, CacheArgs};
2023-11-01 16:46:37 +01:00
use puffin_client::RegistryClientBuilder;
use puffin_dispatch::BuildDispatch;
use puffin_interpreter::Virtualenv;
use puffin_resolver::{Manifest, ResolutionOptions, Resolver};
2023-12-01 21:16:33 +01:00
use pypi_types::IndexUrls;
2023-11-01 16:46:37 +01:00
2023-11-17 19:26:55 +01:00
#[derive(ValueEnum, Default, Clone)]
pub(crate) enum ResolveCliFormat {
#[default]
Compact,
Expanded,
}
2023-11-01 16:46:37 +01:00
#[derive(Parser)]
pub(crate) struct ResolveCliArgs {
requirements: Vec<Requirement>,
/// Write debug output in DOT format for graphviz to this file
2023-11-01 16:46:37 +01:00
#[clap(long)]
graphviz: Option<PathBuf>,
2023-11-08 16:05:15 +01:00
/// Don't build source distributions. This means resolving will not run arbitrary code. The
/// cached wheels of already built source distributions will be reused.
#[clap(long)]
no_build: bool,
2023-11-17 19:26:55 +01:00
#[clap(long, default_value = "compact")]
format: ResolveCliFormat,
2023-11-16 21:49:48 +01:00
#[command(flatten)]
cache_args: CacheArgs,
#[arg(long)]
exclude_newer: Option<DateTime<Utc>>,
2023-11-01 16:46:37 +01:00
}
pub(crate) async fn resolve_cli(args: ResolveCliArgs) -> Result<()> {
let cache = Cache::try_from(args.cache_args)?;
2023-11-01 16:46:37 +01:00
let platform = Platform::current()?;
let venv = Virtualenv::from_env(platform, &cache)?;
let client = RegistryClientBuilder::new(cache.clone()).build();
2023-12-18 11:43:03 -05:00
let index_urls = IndexUrls::default();
2023-11-01 16:46:37 +01:00
let build_dispatch = BuildDispatch::new(
2023-12-18 11:43:03 -05:00
&client,
&cache,
venv.interpreter(),
&index_urls,
2023-12-18 09:42:58 -05:00
venv.python_executable(),
2023-11-08 16:05:15 +01:00
args.no_build,
2023-11-01 16:46:37 +01:00
);
// Copied from `BuildDispatch`
let tags = venv.interpreter().tags()?;
let resolver = Resolver::new(
Manifest::simple(args.requirements.clone()),
ResolutionOptions::default(),
venv.interpreter().markers(),
&tags,
&client,
&build_dispatch,
);
let resolution_graph = resolver.resolve().await.with_context(|| {
format!(
"No solution found when resolving: {}",
args.requirements.iter().map(ToString::to_string).join(", "),
)
})?;
if let Some(graphviz) = args.graphviz {
let mut writer = BufWriter::new(File::create(graphviz)?);
let graphviz = Dot::with_attr_getters(
resolution_graph.petgraph(),
&[DotConfig::NodeNoLabel, DotConfig::EdgeNoLabel],
&|_graph, edge_ref| format!("label={:?}", edge_ref.weight().to_string()),
&|_graph, (_node_index, dist)| {
format!(
"label={:?}",
dist.to_string().replace("==", "\n").to_string()
)
},
);
write!(&mut writer, "{graphviz:?}")?;
}
let mut resolution = resolution_graph.requirements();
2023-11-01 16:54:47 +01:00
resolution.sort_unstable_by(|a, b| a.name.cmp(&b.name));
#[allow(clippy::print_stderr, clippy::ignored_unit_patterns)]
2023-11-17 19:26:55 +01:00
match args.format {
ResolveCliFormat::Compact => {
println!("{}", resolution.iter().map(ToString::to_string).join(" "));
}
ResolveCliFormat::Expanded => {
for package in resolution {
println!("{}", package);
}
}
}
2023-11-01 16:46:37 +01:00
Ok(())
}