Split crates and clean up structure of codebase ()

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Félix Saparelli 2022-08-20 23:24:12 +12:00 committed by GitHub
parent bf700f9012
commit 4b00f5f143
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
88 changed files with 2989 additions and 1423 deletions
crates/bin/src

237
crates/bin/src/entry.rs Normal file
View file

@ -0,0 +1,237 @@
use std::{fs, path::Path, sync::Arc, time::Duration};
use binstall::{
errors::BinstallError,
helpers::{
jobserver_client::LazyJobserverClient, remote::create_reqwest_client,
tasks::AutoAbortJoinHandle,
},
manifests::{
binstall_crates_v1::Records, cargo_crates_v1::CratesToml, cargo_toml_binstall::PkgOverride,
},
ops::{
self,
resolve::{CrateName, Resolution, VersionReqExt},
},
targets::get_desired_targets,
};
use log::{debug, error, info, warn, LevelFilter};
use miette::{miette, Result, WrapErr};
use tokio::task::block_in_place;
use crate::{args::Args, install_path, ui::UIThread};
pub async fn install_crates(mut args: Args, jobserver_client: LazyJobserverClient) -> Result<()> {
let cli_overrides = PkgOverride {
pkg_url: args.pkg_url.take(),
pkg_fmt: args.pkg_fmt.take(),
bin_dir: args.bin_dir.take(),
};
// Launch target detection
let desired_targets = get_desired_targets(&args.targets);
// Initialize reqwest client
let client = create_reqwest_client(args.secure, args.min_tls_version.map(|v| v.into()))?;
// Build crates.io api client
let crates_io_api_client = crates_io_api::AsyncClient::new(
"cargo-binstall (https://github.com/ryankurte/cargo-binstall)",
Duration::from_millis(100),
)
.expect("bug: invalid user agent");
// Initialize UI thread
let mut uithread = UIThread::new(!args.no_confirm);
let (install_path, metadata, temp_dir) = block_in_place(|| -> Result<_> {
// Compute install directory
let (install_path, custom_install_path) =
install_path::get_install_path(args.install_path.as_deref());
let install_path = install_path.ok_or_else(|| {
error!("No viable install path found of specified, try `--install-path`");
miette!("No install path found or specified")
})?;
fs::create_dir_all(&install_path).map_err(BinstallError::Io)?;
debug!("Using install path: {}", install_path.display());
// Load metadata
let metadata = if !custom_install_path {
debug!("Reading binstall/crates-v1.json");
Some(Records::load()?)
} else {
None
};
// Create a temporary directory for downloads etc.
//
// Put all binaries to a temporary directory under `dst` first, catching
// some failure modes (e.g., out of space) before touching the existing
// binaries. This directory will get cleaned up via RAII.
let temp_dir = tempfile::Builder::new()
.prefix("cargo-binstall")
.tempdir_in(&install_path)
.map_err(BinstallError::from)
.wrap_err("Creating a temporary directory failed.")?;
Ok((install_path, metadata, temp_dir))
})?;
// Remove installed crates
let crate_names = CrateName::dedup(&args.crate_names)
.filter_map(|crate_name| {
match (
args.force,
metadata.as_ref().and_then(|records| records.get(&crate_name.name)),
&crate_name.version_req,
) {
(false, Some(metadata), Some(version_req))
if version_req.is_latest_compatible(&metadata.current_version) =>
{
debug!("Bailing out early because we can assume wanted is already installed from metafile");
info!(
"{} v{} is already installed, use --force to override",
crate_name.name, metadata.current_version
);
None
}
// we have to assume that the version req could be *,
// and therefore a remote upgraded version could exist
(false, Some(metadata), _) => {
Some((crate_name, Some(metadata.current_version.clone())))
}
_ => Some((crate_name, None)),
}
})
.collect::<Vec<_>>();
if crate_names.is_empty() {
debug!("Nothing to do");
return Ok(());
}
let temp_dir_path: Arc<Path> = Arc::from(temp_dir.path());
// Create binstall_opts
let binstall_opts = Arc::new(ops::Options {
no_symlinks: args.no_symlinks,
dry_run: args.dry_run,
force: args.force,
version_req: args.version_req.take(),
manifest_path: args.manifest_path.take(),
cli_overrides,
desired_targets,
quiet: args.log_level == LevelFilter::Off,
});
let tasks: Vec<_> = if !args.dry_run && !args.no_confirm {
// Resolve crates
let tasks: Vec<_> = crate_names
.into_iter()
.map(|(crate_name, current_version)| {
AutoAbortJoinHandle::spawn(ops::resolve::resolve(
binstall_opts.clone(),
crate_name,
current_version,
temp_dir_path.clone(),
install_path.clone(),
client.clone(),
crates_io_api_client.clone(),
))
})
.collect();
// Confirm
let mut resolutions = Vec::with_capacity(tasks.len());
for task in tasks {
match task.await?? {
Resolution::AlreadyUpToDate => {}
res => resolutions.push(res),
}
}
if resolutions.is_empty() {
debug!("Nothing to do");
return Ok(());
}
uithread.confirm().await?;
// Install
resolutions
.into_iter()
.map(|resolution| {
AutoAbortJoinHandle::spawn(ops::install::install(
resolution,
binstall_opts.clone(),
jobserver_client.clone(),
))
})
.collect()
} else {
// Resolve crates and install without confirmation
crate_names
.into_iter()
.map(|(crate_name, current_version)| {
let opts = binstall_opts.clone();
let temp_dir_path = temp_dir_path.clone();
let jobserver_client = jobserver_client.clone();
let client = client.clone();
let crates_io_api_client = crates_io_api_client.clone();
let install_path = install_path.clone();
AutoAbortJoinHandle::spawn(async move {
let resolution = ops::resolve::resolve(
opts.clone(),
crate_name,
current_version,
temp_dir_path,
install_path,
client,
crates_io_api_client,
)
.await?;
ops::install::install(resolution, opts, jobserver_client).await
})
})
.collect()
};
let mut metadata_vec = Vec::with_capacity(tasks.len());
for task in tasks {
if let Some(metadata) = task.await?? {
metadata_vec.push(metadata);
}
}
block_in_place(|| {
if let Some(mut records) = metadata {
// If using standardised install path,
// then create_dir_all(&install_path) would also
// create .cargo.
debug!("Writing .crates.toml");
CratesToml::append(metadata_vec.iter())?;
debug!("Writing binstall/crates-v1.json");
for metadata in metadata_vec {
records.replace(metadata);
}
records.overwrite()?;
}
if args.no_cleanup {
// Consume temp_dir without removing it from fs.
temp_dir.into_path();
} else {
temp_dir.close().unwrap_or_else(|err| {
warn!("Failed to clean up some resources: {err}");
});
}
Ok(())
})
}