mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 22:30:03 +00:00
feat: Add new cmdline option --no-track
(#1111)
Same as `cargo-install`'s `--no-track`. It is also passed to `cargo-install` if it is invoked. Also fixed `fs::atomic_symlink_file` which on Windows could fallback to non-atomic install if symlinking failed. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
a849db3ef4
commit
1432093dcc
8 changed files with 194 additions and 43 deletions
|
@ -11,7 +11,10 @@ use tracing::debug;
|
|||
|
||||
use crate::{
|
||||
errors::BinstallError,
|
||||
fs::{atomic_install, atomic_symlink_file},
|
||||
fs::{
|
||||
atomic_install, atomic_install_noclobber, atomic_symlink_file,
|
||||
atomic_symlink_file_noclobber,
|
||||
},
|
||||
helpers::download::ExtractedFiles,
|
||||
manifests::cargo_toml_binstall::{PkgFmt, PkgMeta},
|
||||
};
|
||||
|
@ -180,28 +183,48 @@ impl BinFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn install_bin(&self) -> Result<(), BinstallError> {
|
||||
fn pre_install_bin(&self) -> Result<(), BinstallError> {
|
||||
if !self.source.try_exists()? {
|
||||
return Err(BinstallError::BinFileNotFound(self.source.clone()));
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Atomically install file from '{}' to '{}'",
|
||||
self.source.display(),
|
||||
self.dest.display()
|
||||
);
|
||||
|
||||
#[cfg(unix)]
|
||||
std::fs::set_permissions(
|
||||
&self.source,
|
||||
std::os::unix::fs::PermissionsExt::from_mode(0o755),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_bin(&self) -> Result<(), BinstallError> {
|
||||
self.pre_install_bin()?;
|
||||
|
||||
debug!(
|
||||
"Atomically install file from '{}' to '{}'",
|
||||
self.source.display(),
|
||||
self.dest.display()
|
||||
);
|
||||
|
||||
atomic_install(&self.source, &self.dest)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_bin_noclobber(&self) -> Result<(), BinstallError> {
|
||||
self.pre_install_bin()?;
|
||||
|
||||
debug!(
|
||||
"Installing file from '{}' to '{}' only if dst not exists",
|
||||
self.source.display(),
|
||||
self.dest.display()
|
||||
);
|
||||
|
||||
atomic_install_noclobber(&self.source, &self.dest)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_link(&self) -> Result<(), BinstallError> {
|
||||
if let Some(link) = &self.link {
|
||||
let dest = self.link_dest();
|
||||
|
@ -216,6 +239,20 @@ impl BinFile {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_link_noclobber(&self) -> Result<(), BinstallError> {
|
||||
if let Some(link) = &self.link {
|
||||
let dest = self.link_dest();
|
||||
debug!(
|
||||
"Create link '{}' pointing to '{}' only if dst not exists",
|
||||
link.display(),
|
||||
dest.display()
|
||||
);
|
||||
atomic_symlink_file_noclobber(dest, link)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn link_dest(&self) -> &Path {
|
||||
if cfg!(target_family = "unix") {
|
||||
Path::new(self.dest.file_name().unwrap())
|
||||
|
|
|
@ -3,6 +3,54 @@ use std::{fs, io, path::Path};
|
|||
use tempfile::{NamedTempFile, TempPath};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
fn copy_to_tempfile(src: &Path, dst: &Path) -> io::Result<NamedTempFile> {
|
||||
let mut src_file = fs::File::open(src)?;
|
||||
|
||||
let parent = dst.parent().unwrap();
|
||||
debug!("Creating named tempfile at '{}'", parent.display());
|
||||
let mut tempfile = NamedTempFile::new_in(parent)?;
|
||||
|
||||
debug!(
|
||||
"Copying from '{}' to '{}'",
|
||||
src.display(),
|
||||
tempfile.path().display()
|
||||
);
|
||||
io::copy(&mut src_file, tempfile.as_file_mut())?;
|
||||
|
||||
debug!("Retrieving permissions of '{}'", src.display());
|
||||
let permissions = src_file.metadata()?.permissions();
|
||||
|
||||
debug!(
|
||||
"Setting permissions of '{}' to '{permissions:#?}'",
|
||||
tempfile.path().display()
|
||||
);
|
||||
tempfile.as_file().set_permissions(permissions)?;
|
||||
|
||||
Ok(tempfile)
|
||||
}
|
||||
|
||||
/// Install a file.
|
||||
///
|
||||
/// This is a blocking function, must be called in `block_in_place` mode.
|
||||
pub fn atomic_install_noclobber(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
debug!(
|
||||
"Attempting to rename from '{}' to '{}'.",
|
||||
src.display(),
|
||||
dst.display()
|
||||
);
|
||||
|
||||
let tempfile = copy_to_tempfile(src, dst)?;
|
||||
|
||||
debug!(
|
||||
"Persisting '{}' to '{}', fail if dst already exists",
|
||||
tempfile.path().display(),
|
||||
dst.display()
|
||||
);
|
||||
tempfile.persist_noclobber(dst)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Atomically install a file.
|
||||
///
|
||||
/// This is a blocking function, must be called in `block_in_place` mode.
|
||||
|
@ -33,29 +81,7 @@ pub fn atomic_install(src: &Path, dst: &Path) -> io::Result<()> {
|
|||
// Fallback to creating NamedTempFile on the parent dir of
|
||||
// dst.
|
||||
|
||||
let mut src_file = fs::File::open(src)?;
|
||||
|
||||
let parent = dst.parent().unwrap();
|
||||
debug!("Creating named tempfile at '{}'", parent.display());
|
||||
let mut tempfile = NamedTempFile::new_in(parent)?;
|
||||
|
||||
debug!(
|
||||
"Copying from '{}' to '{}'",
|
||||
src.display(),
|
||||
tempfile.path().display()
|
||||
);
|
||||
io::copy(&mut src_file, tempfile.as_file_mut())?;
|
||||
|
||||
debug!("Retrieving permissions of '{}'", src.display());
|
||||
let permissions = src_file.metadata()?.permissions();
|
||||
|
||||
debug!(
|
||||
"Setting permissions of '{}' to '{permissions:#?}'",
|
||||
tempfile.path().display()
|
||||
);
|
||||
tempfile.as_file().set_permissions(permissions)?;
|
||||
|
||||
persist(tempfile.into_temp_path(), dst)?;
|
||||
persist(copy_to_tempfile(src, dst)?.into_temp_path(), dst)?;
|
||||
} else {
|
||||
debug!("Attempting at atomically succeeded.");
|
||||
}
|
||||
|
@ -63,17 +89,30 @@ pub fn atomic_install(src: &Path, dst: &Path) -> io::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn symlink_file(original: &Path, link: &Path) -> io::Result<()> {
|
||||
fn symlink_file_inner(dest: &Path, link: &Path) -> io::Result<()> {
|
||||
#[cfg(target_family = "unix")]
|
||||
std::os::unix::fs::symlink(original, link)?;
|
||||
std::os::unix::fs::symlink(dest, link)?;
|
||||
|
||||
// Symlinks on Windows are disabled in some editions, so creating one is unreliable.
|
||||
#[cfg(target_family = "windows")]
|
||||
std::os::windows::fs::symlink_file(original, link)
|
||||
.or_else(|_| std::fs::copy(original, link).map(drop))?;
|
||||
std::os::windows::fs::symlink_file(dest, link)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn atomic_symlink_file_noclobber(dest: &Path, link: &Path) -> io::Result<()> {
|
||||
match symlink_file_inner(dest, link) {
|
||||
Ok(_) => Ok(()),
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
// Symlinks on Windows are disabled in some editions, so creating one is unreliable.
|
||||
// Fallback to copy if it fails.
|
||||
Err(_) => atomic_install_noclobber(dest, link),
|
||||
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Atomically install symlink "link" to a file "dst".
|
||||
///
|
||||
/// This is a blocking function, must be called in `block_in_place` mode.
|
||||
|
@ -82,6 +121,8 @@ pub fn atomic_symlink_file(dest: &Path, link: &Path) -> io::Result<()> {
|
|||
|
||||
debug!("Creating tempPath at '{}'", parent.display());
|
||||
let temp_path = NamedTempFile::new_in(parent)?.into_temp_path();
|
||||
// Remove this file so that we can create a symlink
|
||||
// with the name.
|
||||
fs::remove_file(&temp_path)?;
|
||||
|
||||
debug!(
|
||||
|
@ -89,9 +130,18 @@ pub fn atomic_symlink_file(dest: &Path, link: &Path) -> io::Result<()> {
|
|||
temp_path.display(),
|
||||
dest.display()
|
||||
);
|
||||
symlink_file(dest, &temp_path)?;
|
||||
|
||||
persist(temp_path, link)
|
||||
match symlink_file_inner(dest, &temp_path) {
|
||||
Ok(_) => persist(temp_path, link),
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
// Symlinks on Windows are disabled in some editions, so creating one is unreliable.
|
||||
// Fallback to copy if it fails.
|
||||
Err(_) => atomic_install(dest, link),
|
||||
|
||||
#[cfg(not(target_family = "windows"))]
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn persist(temp_path: TempPath, to: &Path) -> io::Result<()> {
|
||||
|
|
|
@ -25,6 +25,7 @@ pub struct Options {
|
|||
pub force: bool,
|
||||
pub quiet: bool,
|
||||
pub locked: bool,
|
||||
pub no_track: bool,
|
||||
|
||||
pub version_req: Option<VersionReq>,
|
||||
pub manifest_path: Option<PathBuf>,
|
||||
|
|
|
@ -51,15 +51,26 @@ impl Resolution {
|
|||
|
||||
impl ResolutionFetch {
|
||||
pub fn install(self, opts: &Options) -> Result<CrateInfo, BinstallError> {
|
||||
type InstallFp = fn(&bins::BinFile) -> Result<(), BinstallError>;
|
||||
|
||||
let (install_bin, install_link): (InstallFp, InstallFp) = match (opts.no_track, opts.force)
|
||||
{
|
||||
(true, true) | (false, _) => (bins::BinFile::install_bin, bins::BinFile::install_link),
|
||||
(true, false) => (
|
||||
bins::BinFile::install_bin_noclobber,
|
||||
bins::BinFile::install_link_noclobber,
|
||||
),
|
||||
};
|
||||
|
||||
info!("Installing binaries...");
|
||||
for file in &self.bin_files {
|
||||
file.install_bin()?;
|
||||
install_bin(file)?;
|
||||
}
|
||||
|
||||
// Generate symlinks
|
||||
if !opts.no_symlinks {
|
||||
for file in &self.bin_files {
|
||||
file.install_link()?;
|
||||
install_link(file)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,6 +169,10 @@ impl ResolutionSource {
|
|||
cmd.arg("--root").arg(cargo_root);
|
||||
}
|
||||
|
||||
if opts.no_track {
|
||||
cmd.arg("--no-track");
|
||||
}
|
||||
|
||||
if !opts.dry_run {
|
||||
let mut child = opts
|
||||
.jobserver_client
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue