diff --git a/.github/bors.toml b/.github/bors.toml index fc2073541..cc44b91ce 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -1,5 +1,6 @@ block_labels = ["needs-decision"] delete_merged_branches = true required_approvals = 1 +use_codeowners = true status = ["conclusion"] timeout_sec = 21600 diff --git a/CHANGELOG.md b/CHANGELOG.md index c15ab9f9b..d412c97a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- #654 - Use color-eyre for error reporting - #658 - Upgrade dependencies - #647 - Add `mips64-unknown-linux-muslabi64` and `mips64el-unknown-linux-muslabi64` support - #652 - Allow trying individual targets via bors. diff --git a/Cargo.lock b/Cargo.lock index 15950f918..c588c6d33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,12 +67,40 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color-eyre" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ebf286c900a6d5867aeff75cfee3192857bb7f24b547d4f0df2ed6baa812c90" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "cross" version = "0.2.1" dependencies = [ "atty", - "error-chain", + "color-eyre", + "eyre", "home", "lazy_static", "libc", @@ -92,13 +120,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] -name = "error-chain" -version = "0.12.4" +name = "eyre" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +checksum = "9289ed2c0440a6536e65119725cf91fc2c6b5e513bfd2e36e1134d7cca6ca12f" dependencies = [ - "backtrace", - "version_check", + "indenter", + "once_cell", ] [[package]] @@ -125,6 +153,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "itoa" version = "1.0.1" @@ -190,6 +224,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "owo-colors" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20448fd678ec04e6ea15bbe0476874af65e98a01515d667aa49f1434dc44ebf4" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -234,12 +286,30 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + [[package]] name = "shell-escape" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" +[[package]] +name = "thread_local" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +dependencies = [ + "once_cell", +] + [[package]] name = "toml" version = "0.5.8" @@ -250,10 +320,52 @@ dependencies = [ ] [[package]] -name = "version_check" -version = "0.9.4" +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +dependencies = [ + "lazy_static", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "valuable" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "which" diff --git a/Cargo.toml b/Cargo.toml index 63f4cd242..a312f7c51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,21 +12,22 @@ edition = "2018" [dependencies] atty = "0.2" -error-chain = "0.12" +color-eyre = "0.6" +eyre = "0.6" home = "0.5" -lazy_static = "1.0" -libc = "0.2.104" +lazy_static = "1" rustc_version = "0.4" toml = "0.5" which = { version = "4", default_features = false } -shell-escape = "0.1.4" -serde_json = "1.0.48" +shell-escape = "0.1" +serde_json = "1" [target.'cfg(not(windows))'.dependencies] nix = "0.23" +libc = "0.2" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.7", features = ["winbase"] } +winapi = { version = "0.3", features = ["winbase"] } [profile.release] lto = true diff --git a/src/cargo.rs b/src/cargo.rs index 0ed1ccac1..14e9585e6 100644 --- a/src/cargo.rs +++ b/src/cargo.rs @@ -65,7 +65,7 @@ impl Root { /// Cargo project root pub fn root() -> Result> { - let cd = env::current_dir().chain_err(|| "couldn't get current directory")?; + let cd = env::current_dir().wrap_err("couldn't get current directory")?; let mut dir = &*cd; loop { diff --git a/src/config.rs b/src/config.rs index 381b97c62..4d5232007 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,9 @@ use crate::{Result, Target, Toml}; +use crate::errors::*; use std::collections::HashMap; use std::env; + #[derive(Debug)] struct Environment(&'static str, Option>); @@ -42,22 +44,20 @@ impl Environment { self.get_build_var("XARGO"), self.get_target_var(target, "XARGO"), ); - let build_env = - if let Some(value) = build_xargo { - Some(value.parse::().map_err(|_| { - format!("error parsing {} from XARGO environment variable", value) - })?) - } else { - None - }; - let target_env = - if let Some(value) = target_xargo { - Some(value.parse::().map_err(|_| { - format!("error parsing {} from XARGO environment variable", value) - })?) - } else { - None - }; + let build_env = if let Some(value) = build_xargo { + Some(value.parse::().wrap_err_with(|| { + format!("error parsing {} from XARGO environment variable", value) + })?) + } else { + None + }; + let target_env = if let Some(value) = target_xargo { + Some(value.parse::().wrap_err_with(|| { + format!("error parsing {} from XARGO environment variable", value) + })?) + } else { + None + }; Ok((build_env, target_env)) } @@ -279,10 +279,9 @@ mod tests { fn toml(content: &str) -> Result { Ok(crate::Toml { - table: if let Ok(toml::Value::Table(table)) = content.parse() { - table - } else { - return Err("couldn't parse toml as TOML table".into()); + table: match content.parse().wrap_err("couldn't parse toml")? { + toml::Value::Table(table) => table, + _ => eyre::bail!("couldn't parse toml as TOML table"), }, }) } diff --git a/src/docker.rs b/src/docker.rs index 25fc3f7be..953fe00d4 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -2,41 +2,35 @@ use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::{env, fs}; -use atty::Stream; -use error_chain::bail; - use crate::cargo::Root; use crate::errors::*; use crate::extensions::{CommandExt, SafeCommand}; use crate::id; use crate::{Config, Target}; +use atty::Stream; +use eyre::bail; const DOCKER_IMAGES: &[&str] = &include!(concat!(env!("OUT_DIR"), "/docker-images.rs")); const CROSS_IMAGE: &str = "ghcr.io/cross-rs"; const DOCKER: &str = "docker"; const PODMAN: &str = "podman"; -fn get_container_engine() -> Result { - let container_engine = env::var("CROSS_CONTAINER_ENGINE").unwrap_or_default(); - - if container_engine.is_empty() { - which::which(DOCKER) - .or_else(|_| which::which(PODMAN)) - .map_err(|e| e.into()) +fn get_container_engine() -> Result { + if let Ok(ce) = env::var("CROSS_CONTAINER_ENGINE") { + which::which(ce) } else { - which::which(container_engine).map_err(|e| e.into()) + which::which(DOCKER).or_else(|_| which::which(PODMAN)) } } pub fn docker_command(subcommand: &str) -> Result { - if let Ok(ce) = get_container_engine() { - let mut command = Command::new(ce); - command.arg(subcommand); - command.args(&["--userns", "host"]); - Ok(command) - } else { - Err("no container engine found; install docker or podman".into()) - } + let ce = get_container_engine() + .map_err(|_| eyre::eyre!("no container engine found")) + .with_suggestion(|| "is docker or podman installed?")?; + let mut command = Command::new(ce); + command.arg(subcommand); + command.args(&["--userns", "host"]); + Ok(command) } /// Register binfmt interpreters @@ -77,7 +71,7 @@ pub fn run( }; let root = root.path(); - let home_dir = home::home_dir().ok_or("could not find home directory")?; + let home_dir = home::home_dir().ok_or_else(|| eyre::eyre!("could not find home directory"))?; let cargo_dir = home::cargo_home()?; let xargo_dir = env::var_os("XARGO_HOME") .map(PathBuf::from) @@ -233,11 +227,7 @@ pub fn image(config: &Config, target: &Target) -> Result { } fn docker_read_mount_paths() -> Result> { - let hostname = if let Ok(v) = env::var("HOSTNAME") { - Ok(v) - } else { - Err("HOSTNAME environment variable not found") - }?; + let hostname = env::var("HOSTNAME").wrap_err("HOSTNAME environment variable not found")?; let docker_path = which::which(DOCKER)?; let mut docker: Command = { @@ -248,12 +238,7 @@ fn docker_read_mount_paths() -> Result> { }; let output = docker.run_and_get_stdout(false)?; - let info = if let Ok(val) = serde_json::from_str(&output) { - Ok(val) - } else { - Err("failed to parse docker inspect output") - }?; - + let info = serde_json::from_str(&output).wrap_err("failed to parse docker inspect output")?; dockerinfo_parse_mounts(&info) } @@ -268,20 +253,20 @@ fn dockerinfo_parse_root_mount_path(info: &serde_json::Value) -> Result Result<()> { + color_eyre::config::HookBuilder::new() + .display_env_section(false) + .install() } diff --git a/src/extensions.rs b/src/extensions.rs index cd90269d7..e33f7c463 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -23,7 +23,7 @@ impl CommandExt for Command { if status.success() { Ok(()) } else { - Err(format!("`{:?}` failed with exit code: {:?}", self, status.code()).into()) + eyre::bail!("`{:?}` failed with exit code: {:?}", self, status.code()) } } @@ -37,7 +37,7 @@ impl CommandExt for Command { fn run_and_get_status(&mut self, verbose: bool) -> Result { self.print_verbose(verbose); self.status() - .chain_err(|| format!("couldn't execute `{:?}`", self)) + .wrap_err_with(|| format!("couldn't execute `{:?}`", self)) } /// Runs the command to completion and returns its stdout @@ -45,11 +45,11 @@ impl CommandExt for Command { self.print_verbose(verbose); let out = self .output() - .chain_err(|| format!("couldn't execute `{:?}`", self))?; + .wrap_err_with(|| format!("couldn't execute `{:?}`", self))?; self.status_result(out.status)?; - String::from_utf8(out.stdout).chain_err(|| format!("`{:?}` output was not UTF-8", self)) + String::from_utf8(out.stdout).wrap_err_with(|| format!("`{:?}` output was not UTF-8", self)) } } diff --git a/src/file.rs b/src/file.rs index b1843478d..2386cef53 100644 --- a/src/file.rs +++ b/src/file.rs @@ -14,8 +14,8 @@ where fn read_(path: &Path) -> Result { let mut s = String::new(); File::open(path) - .chain_err(|| format!("couldn't open {}", path.display()))? + .wrap_err_with(|| format!("couldn't open {}", path.display()))? .read_to_string(&mut s) - .chain_err(|| format!("couldn't read {}", path.display()))?; + .wrap_err_with(|| format!("couldn't read {}", path.display()))?; Ok(s) } diff --git a/src/interpreter.rs b/src/interpreter.rs index ed0daade7..0968093c0 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -7,7 +7,7 @@ use crate::Target; /// Checks if the interpreters have been registered in the host system pub fn is_registered(target: &Target) -> Result { if file::read("/proc/sys/fs/binfmt_misc/status")?.trim() != "enabled" { - return Err("host system doesn't have binfmt_misc support".into()); + eyre::bail!("host system doesn't have binfmt_misc support") } let ok = if target.is_windows() { diff --git a/src/main.rs b/src/main.rs index aa6c5b180..83705a4cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,10 +12,9 @@ mod interpreter; mod rustc; mod rustup; -use std::io::Write; +use std::env; use std::path::PathBuf; use std::process::ExitStatus; -use std::{env, io, process}; use config::Config; use toml::{value::Table, Value}; @@ -228,38 +227,10 @@ impl From for Target { } } -pub fn main() { - fn show_backtrace() -> bool { - env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1") - } - - match run() { - Err(e) => { - let stderr = io::stderr(); - let mut stderr = stderr.lock(); - - writeln!(stderr, "error: {}", e).ok(); - - for e in e.iter().skip(1) { - writeln!(stderr, "caused by: {}", e).ok(); - } - - if show_backtrace() { - if let Some(backtrace) = e.backtrace() { - writeln!(stderr, "{:?}", backtrace).ok(); - } - } else { - writeln!(stderr, "note: run with `RUST_BACKTRACE=1` for a backtrace").ok(); - } - - process::exit(1) - } - Ok(status) => { - if !status.success() { - process::exit(status.code().unwrap_or(1)) - } - } - } +pub fn main() -> Result<()> { + install_panic_hook()?; + run()?; + Ok(()) } fn run() -> Result { @@ -279,7 +250,7 @@ fn run() -> Result { .any(|a| a == "--verbose" || a == "-v" || a == "-vv"); let version_meta = - rustc_version::version_meta().chain_err(|| "couldn'toml.t fetch the `rustc` version")?; + rustc_version::version_meta().wrap_err("couldn't fetch the `rustc` version")?; if let Some(root) = cargo::root()? { let host = version_meta.host(); @@ -294,7 +265,7 @@ fn run() -> Result { let default_toolchain = sysroot .file_name() .and_then(|file_name| file_name.to_str()) - .ok_or("couldn't get toolchain name")?; + .ok_or_else(|| eyre::eyre!("couldn't get toolchain name"))?; let toolchain = if let Some(channel) = args.channel { [channel] .iter() @@ -419,7 +390,7 @@ impl Toml { Ok(Some( value .as_str() - .ok_or_else(|| format!("target.{}.image must be a string", triple))? + .ok_or_else(|| eyre::eyre!("target.{triple}.image must be a string"))? .to_string(), )) } else { @@ -439,7 +410,7 @@ impl Toml { { let value = value .as_str() - .ok_or_else(|| format!("target.{}.runner must be a string", triple))? + .ok_or_else(|| eyre::eyre!("target.{triple}.runner must be a string"))? .to_string(); Ok(Some(value)) } else { @@ -453,7 +424,11 @@ impl Toml { if let Some(value) = self.table.get("build").and_then(|b| b.get("xargo")) { return Ok(( - Some(value.as_bool().ok_or("build.xargo must be a boolean")?), + Some( + value + .as_bool() + .ok_or_else(|| eyre::eyre!("build.xargo must be a boolean"))?, + ), None, )); } @@ -469,7 +444,7 @@ impl Toml { Some( value .as_bool() - .ok_or_else(|| format!("target.{}.xargo must be a boolean", triple))?, + .ok_or_else(|| eyre::eyre!("target.{triple}.xargo must be a boolean"))?, ), )) } else { @@ -500,45 +475,41 @@ impl Toml { fn target_env(&self, target: &Target, key: &str) -> Result> { let triple = target.triple(); - match self + if let Some(&Value::Array(ref vec)) = self .table .get("target") .and_then(|t| t.get(triple)) .and_then(|t| t.get("env")) .and_then(|e| e.get(key)) { - Some(&Value::Array(ref vec)) => vec - .iter() + vec.iter() .map(|val| { val.as_str().ok_or_else(|| { - format!( - "every target.{}.env.{} element must be a string", - triple, key - ) - .into() + eyre::eyre!("every target.{triple}.env.{key} element must be a string",) }) }) - .collect(), - _ => Ok(Vec::new()), + .collect() + } else { + Ok(Vec::new()) } } fn build_env(&self, key: &str) -> Result> { - match self + if let Some(&Value::Array(ref vec)) = self .table .get("build") .and_then(|b| b.get("env")) .and_then(|e| e.get(key)) { - Some(&Value::Array(ref vec)) => vec - .iter() + vec.iter() .map(|val| { val.as_str().ok_or_else(|| { - format!("every build.env.{} element must be a string", key).into() + eyre::eyre!("every build.env.{key} element must be a string") }) }) - .collect(), - _ => Ok(Vec::new()), + .collect() + } else { + Ok(Vec::new()) } } } @@ -552,13 +523,10 @@ fn toml(root: &Root) -> Result> { }; if path.exists() { - Ok(Some(Toml { - table: if let Ok(Value::Table(table)) = file::read(&path)?.parse() { - table - } else { - return Err(format!("couldn't parse {} as TOML table", path.display()).into()); - }, - })) + let content = file::read(&path) + .wrap_err_with(|| format!("could not read file `{}`", path.display()))?; + parse_toml(&content) + .wrap_err_with(|| format!("failed to parse file `{}` as TOML", path.display())) } else { // Let's check if there is a lower case version of the file if root.path().join("cross.toml").exists() { @@ -567,3 +535,12 @@ fn toml(root: &Root) -> Result> { Ok(None) } } + +fn parse_toml(content: &str) -> Result> { + Ok(Some(crate::Toml { + table: match content.parse()? { + toml::Value::Table(table) => table, + _ => eyre::bail!("couldn't parse as TOML table"), + }, + })) +} diff --git a/src/rustup.rs b/src/rustup.rs index 07c37f11f..4dd963192 100644 --- a/src/rustup.rs +++ b/src/rustup.rs @@ -71,7 +71,7 @@ pub fn install_toolchain(toolchain: &str, verbose: bool) -> Result<()> { Command::new("rustup") .args(&["toolchain", "add", toolchain, "--profile", "minimal"]) .run(verbose) - .chain_err(|| format!("couldn't install toolchain `{}`", toolchain)) + .wrap_err_with(|| format!("couldn't install toolchain `{}`", toolchain)) } pub fn install(target: &Target, toolchain: &str, verbose: bool) -> Result<()> { @@ -80,14 +80,14 @@ pub fn install(target: &Target, toolchain: &str, verbose: bool) -> Result<()> { Command::new("rustup") .args(&["target", "add", target, "--toolchain", toolchain]) .run(verbose) - .chain_err(|| format!("couldn't install `std` for {}", target)) + .wrap_err_with(|| format!("couldn't install `std` for {}", target)) } pub fn install_component(component: &str, toolchain: &str, verbose: bool) -> Result<()> { Command::new("rustup") .args(&["component", "add", component, "--toolchain", toolchain]) .run(verbose) - .chain_err(|| format!("couldn't install the `{}` component", component)) + .wrap_err_with(|| format!("couldn't install the `{}` component", component)) } pub fn component_is_installed(component: &str, toolchain: &str, verbose: bool) -> Result {