diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..dcc19c6 --- /dev/null +++ b/build.rs @@ -0,0 +1,39 @@ +use std::env; +use std::process::Command; +use std::str; + +fn main() { + let compiler = match rustc_version() { + Some(compiler) => compiler, + None => return, + }; + + if compiler.nightly { + println!("cargo:rustc-cfg=backtrace"); + } +} + +struct Compiler { + nightly: bool, +} + +fn rustc_version() -> Option { + let rustc = match env::var_os("RUSTC") { + Some(rustc) => rustc, + None => return None, + }; + + let output = match Command::new(rustc).arg("--version").output() { + Ok(output) => output, + Err(_) => return None, + }; + + let version = match str::from_utf8(&output.stdout) { + Ok(version) => version, + Err(_) => return None, + }; + + Some(Compiler { + nightly: version.contains("nightly") || version.contains("dev"), + }) +} diff --git a/src/context.rs b/src/context.rs index 74b3790..49bc54d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,8 +1,9 @@ -use std::backtrace::Backtrace; +use crate::Error; use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; -use crate::Error; +#[cfg(backtrace)] +use std::backtrace::Backtrace; /// Provides the `context` method for `Result`. pub trait Context { @@ -89,6 +90,7 @@ where E: StdError + 'static, C: Display, { + #[cfg(backtrace)] fn backtrace(&self) -> Option<&Backtrace> { self.error.backtrace() } @@ -102,6 +104,7 @@ impl StdError for ContextError where C: Display, { + #[cfg(backtrace)] fn backtrace(&self) -> Option<&Backtrace> { Some(self.error.backtrace()) } diff --git a/src/error.rs b/src/error.rs index 211cc3e..dba442f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,12 +1,14 @@ use crate::context::ContextError; use std::any::TypeId; -use std::backtrace::{Backtrace, BacktraceStatus}; use std::error::Error as StdError; use std::fmt::{self, Debug, Display}; use std::mem; use std::ops::{Deref, DerefMut}; use std::ptr; +#[cfg(backtrace)] +use std::backtrace::{Backtrace, BacktraceStatus}; + /// The `Error` type, a wrapper around a dynamic error type. /// /// `Error` functions a lot like `Box`, with these @@ -48,6 +50,7 @@ impl Error { where E: StdError + Send + Sync + 'static, { + #[cfg(backtrace)] let backtrace = match error.backtrace() { Some(_) => None, None => Some(Backtrace::capture()), @@ -59,6 +62,7 @@ impl Error { let inner = Box::new(ErrorImpl { vtable, type_id, + #[cfg(backtrace)] backtrace, error, }); @@ -90,6 +94,12 @@ impl Error { } /// Get the backtrace for this Error. + /// + /// Backtraces are only available on the nightly channel. Tracking issue: + /// [rust-lang/rust#53487][tracking]. + /// + /// [tracking]: https://github.com/rust-lang/rust/issues/53487 + #[cfg(backtrace)] pub fn backtrace(&self) -> &Backtrace { // NB: this unwrap can only fail if the underlying error's backtrace // method is nondeterministic, which would only happen in maliciously @@ -197,18 +207,21 @@ impl Debug for Error { } } - let backtrace = self.backtrace(); - match backtrace.status() { - BacktraceStatus::Captured => { - writeln!(f, "\n{}", backtrace)?; + #[cfg(backtrace)] + { + let backtrace = self.backtrace(); + match backtrace.status() { + BacktraceStatus::Captured => { + writeln!(f, "\n{}", backtrace)?; + } + BacktraceStatus::Disabled => { + writeln!( + f, + "\nbacktrace disabled; run with RUST_LIB_BACKTRACE=1 environment variable to display a backtrace" + )?; + } + _ => {} } - BacktraceStatus::Disabled => { - writeln!( - f, - "\nbacktrace disabled; run with RUST_LIB_BACKTRACE=1 environment variable to display a backtrace" - )?; - } - _ => {} } Ok(()) @@ -235,6 +248,7 @@ impl Drop for Error { struct ErrorImpl { vtable: *const (), type_id: TypeId, + #[cfg(backtrace)] backtrace: Option, error: E, } diff --git a/src/lib.rs b/src/lib.rs index 0eda037..5bf2070 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(backtrace)] +#![cfg_attr(backtrace, feature(backtrace))] mod context; mod error;