rustup_toolchain/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
//! Utilities for working with `rustup` toolchains.
//!
//! # Ensuring a toolchain is installed
//!
//! This checks if a toolchain is installed, and installs it if not.
//!
//! ```no_run
//! rustup_toolchain::ensure_installed("nightly").unwrap();
//! ```
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
/// Enumerates all errors that can currently occur within this crate.
pub enum Error {
/// Some kind of IO error occurred.
#[error(transparent)]
IoError(#[from] std::io::Error),
/// The lock used to work around <https://github.com/rust-lang/rustup/issues/988> has been poisoned
#[error("The lock used to work around https://github.com/rust-lang/rustup/issues/988 has been poisoned")]
StdSyncPoisonError,
/// `rustup toolchain install ...` failed for some reason
#[error("`rustup toolchain install ...` failed for some reason")]
RustupToolchainInstallError,
}
/// Shorthand for [`std::result::Result<T, rustup_toolchain::Error>`].
pub type Result<T> = std::result::Result<T, Error>;
/// As a workaround for [Rustup (including proxies) is not safe for concurrent
/// use](https://github.com/rust-lang/rustup/issues/988) we keep a per-process
/// global lock.
static RUSTUP_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
/// Installs a toolchain if it is not already installed.
///
/// As a workaround for [Rustup (including proxies) is not safe for concurrent
/// use](https://github.com/rust-lang/rustup/issues/988) this function is
/// protected by a process-global lock. If you use multiple processes, you need
/// to prevent concurrent `rustup` usage yourself.
///
/// # Errors
///
/// If `rustup` is not installed on your system, for example.
pub fn install(toolchain: impl AsRef<str>) -> Result<()> {
// The reason we check if the toolchain is installed rather than always
// doing `rustup install toolchain` is because otherwise there will be noisy
// "already installed" output from `rustup install toolchain`.
if !is_installed(toolchain.as_ref())? {
run_rustup_install(toolchain)?;
}
Ok(())
}
/// Deprecated
#[deprecated(since = "0.1.4", note = "Renamed to `install()` for brevity.")]
pub fn ensure_installed(toolchain: &str) -> Result<()> {
install(toolchain)
}
/// Check if a toolchain is installed.
///
/// As a workaround [Rustup (including proxies) is not safe for concurrent
/// use](https://github.com/rust-lang/rustup/issues/988) this function is
/// protected by a process-global lock. If you use multiple processes, you need
/// to prevent concurrent `rustup` usage yourself.
///
/// # Errors
///
/// If `rustup` is not installed on your system, for example.
pub fn is_installed(toolchain: &str) -> Result<bool> {
let _guard = RUSTUP_MUTEX.lock().map_err(|_| Error::StdSyncPoisonError)?;
Ok(std::process::Command::new("rustup")
.arg("run")
.arg(toolchain)
.arg("cargo")
.arg("--version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()?
.success())
}
fn run_rustup_install(toolchain: impl AsRef<str>) -> Result<()> {
let _guard = RUSTUP_MUTEX.lock().map_err(|_| Error::StdSyncPoisonError)?;
let status = std::process::Command::new("rustup")
.arg("toolchain")
.arg("install")
.arg("--no-self-update")
.arg("--profile")
.arg("minimal")
.arg(toolchain.as_ref())
.status()?;
if status.success() {
Ok(())
} else {
Err(Error::RustupToolchainInstallError)
}
}