use std::env; use std::ffi::OsString; use std::fs; use std::path::Path; use std::process::{Command, ExitStatus}; use std::str; // This code exercises the surface area that we expect of the std Backtrace // type. If the current toolchain is able to compile it, we go ahead and use // backtrace in eyre. const BACKTRACE_PROBE: &str = r#" #![feature(backtrace)] #![allow(dead_code)] use std::backtrace::{Backtrace, BacktraceStatus}; use std::error::Error; use std::fmt::{self, Display}; #[derive(Debug)] struct E; impl Display for E { fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result { unimplemented!() } } impl Error for E { fn backtrace(&self) -> Option<&Backtrace> { let backtrace = Backtrace::capture(); match backtrace.status() { BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {} } unimplemented!() } } "#; const TRACK_CALLER_PROBE: &str = r#" #![allow(dead_code)] #[track_caller] fn foo() { let _location = std::panic::Location::caller(); } "#; fn main() { match compile_probe(BACKTRACE_PROBE) { Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"), _ => {} } match compile_probe(TRACK_CALLER_PROBE) { Some(status) if status.success() => println!("cargo:rustc-cfg=track_caller"), _ => {} } let version = match rustc_version_info() { Some(version) => version, None => return, }; version.toolchain.set_feature(); if version.minor < 52 { println!("cargo:rustc-cfg=eyre_no_fmt_arguments_as_str"); } if version.minor < 58 { println!("cargo:rustc-cfg=eyre_no_fmt_args_capture"); } } fn compile_probe(probe: &str) -> Option { let rustc = env::var_os("RUSTC")?; let out_dir = env::var_os("OUT_DIR")?; let probefile = Path::new(&out_dir).join("probe.rs"); fs::write(&probefile, probe).ok()?; Command::new(rustc) .arg("--edition=2018") .arg("--crate-name=eyre_build") .arg("--crate-type=lib") .arg("--emit=metadata") .arg("--out-dir") .arg(out_dir) .arg(probefile) .status() .ok() } // TODO factor this toolchain parsing and related tests into its own file #[derive(PartialEq)] enum Toolchain { Stable, Beta, Nightly, } impl Toolchain { fn set_feature(self) { match self { Toolchain::Nightly => println!("cargo:rustc-cfg=nightly"), Toolchain::Beta => println!("cargo:rustc-cfg=beta"), Toolchain::Stable => println!("cargo:rustc-cfg=stable"), } } } struct VersionInfo { minor: u32, toolchain: Toolchain, } fn rustc_version_info() -> Option { let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); let output = Command::new(rustc).arg("--version").output().ok()?; let version = str::from_utf8(&output.stdout).ok()?; let mut pieces = version.split(['.', ' ', '-']); if pieces.next() != Some("rustc") { return None; } let _major: u32 = pieces.next()?.parse().ok()?; let minor = pieces.next()?.parse().ok()?; let _patch: u32 = pieces.next()?.parse().ok()?; let toolchain = match pieces.next() { Some("beta") => Toolchain::Beta, Some("nightly") => Toolchain::Nightly, _ => Toolchain::Stable, }; let version = VersionInfo { minor, toolchain }; Some(version) }