From 10b0eb1a459786d34b2dce7b5e368d9b00d9623e Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Sat, 2 Jul 2022 12:54:15 -0500 Subject: [PATCH] Better error diagnostics on targets without std. Provides a warning if a docker command fails and `rust-std` is not available for the target. For example, for `x86_64-unknown-dragonfly`, the added output would be: ```bash [cross] warning: rust-std is not available for x86_64-unknown-dragonfly [cross] note: you may need to build components for the target via `-Z build-std=` or in your cross configuration specify `target.x86_64-unknown-dragonfly.build-std` the available components are core, std, alloc, and proc_macro ``` This is done solely if the command fails and if the target is a built-in, so we don't get misleading warnings for custom targets, and there is no overhead if the build succeeds. --- src/lib.rs | 21 +++++++++++++++++++++ src/rustup.rs | 44 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 038d674cf..e7999df15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,6 +360,24 @@ impl Serialize for Target { } } +fn warn_on_failure(target: &Target, toolchain: &str, msg_info: MessageInfo) -> Result<()> { + let rust_std = format!("rust-std-{target}"); + if target.is_builtin() { + let component = rustup::check_component(&rust_std, toolchain, msg_info)?; + if component.is_not_available() { + shell::warn(format!("rust-std is not available for {target}"), msg_info)?; + shell::note( + format_args!( + r#"you may need to build components for the target via `-Z build-std=` or in your cross configuration specify `target.{target}.build-std` + the available components are core, std, alloc, and proc_macro"# + ), + msg_info, + )?; + } + } + Ok(()) +} + pub fn run() -> Result { let target_list = rustc::target_list(Verbosity::Quiet.into())?; let args = cli::parse(&target_list)?; @@ -524,6 +542,9 @@ pub fn run() -> Result { .subcommand .map(|sc| sc.needs_host(is_remote)) .unwrap_or(false); + if !status.success() { + warn_on_failure(&target, &toolchain, args.msg_info)?; + } if !(status.success() && needs_host) { return Ok(status); } diff --git a/src/rustup.rs b/src/rustup.rs index 5825bb9f9..4872eb855 100644 --- a/src/rustup.rs +++ b/src/rustup.rs @@ -120,16 +120,52 @@ pub fn install_component(component: &str, toolchain: &str, msg_info: MessageInfo .wrap_err_with(|| format!("couldn't install the `{component}` component")) } -pub fn component_is_installed( - component: &str, +pub enum Component<'a> { + Installed(&'a str), + Available(&'a str), + NotAvailable(&'a str), +} + +impl<'a> Component<'a> { + pub fn is_installed(&'a self) -> bool { + matches!(self, Component::Installed(_)) + } + + pub fn is_not_available(&'a self) -> bool { + matches!(self, Component::NotAvailable(_)) + } +} + +pub fn check_component<'a>( + component: &'a str, toolchain: &str, msg_info: MessageInfo, -) -> Result { +) -> Result> { Ok(Command::new("rustup") .args(&["component", "list", "--toolchain", toolchain]) .run_and_get_stdout(msg_info)? .lines() - .any(|l| l.starts_with(component) && l.contains("installed"))) + .find_map(|line| { + let available = line.starts_with(component); + let installed = line.contains("installed"); + match available { + true => Some(installed), + false => None, + } + }) + .map(|installed| match installed { + true => Component::Installed(component), + false => Component::Available(component), + }) + .unwrap_or_else(|| Component::NotAvailable(component))) +} + +pub fn component_is_installed( + component: &str, + toolchain: &str, + msg_info: MessageInfo, +) -> Result { + Ok(check_component(component, toolchain, msg_info)?.is_installed()) } fn rustc_channel(version: &Version) -> Result {