1 #![allow(clippy::option_if_let_else)]
2
3 use std::env;
4 use std::fs;
5 use std::path::Path;
6 use std::process::{Command, ExitStatus, Stdio};
7 use std::str;
8
9 #[cfg(all(feature = "backtrace", not(feature = "std")))]
10 compile_error! {
11 "`backtrace` feature without `std` feature is not supported"
12 }
13
14 // This code exercises the surface area that we expect of the std Backtrace
15 // type. If the current toolchain is able to compile it, we go ahead and use
16 // backtrace in anyhow.
17 const PROBE: &str = r#"
18 #![feature(backtrace)]
19 #![allow(dead_code)]
20
21 use std::backtrace::{Backtrace, BacktraceStatus};
22 use std::error::Error;
23 use std::fmt::{self, Display};
24
25 #[derive(Debug)]
26 struct E;
27
28 impl Display for E {
29 fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
30 unimplemented!()
31 }
32 }
33
34 impl Error for E {
35 fn backtrace(&self) -> Option<&Backtrace> {
36 let backtrace = Backtrace::capture();
37 match backtrace.status() {
38 BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
39 }
40 unimplemented!()
41 }
42 }
43 "#;
44
main()45 fn main() {
46 if cfg!(feature = "std") {
47 match compile_probe() {
48 Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
49 _ => {}
50 }
51 }
52
53 let rustc = match rustc_minor_version() {
54 Some(rustc) => rustc,
55 None => return,
56 };
57
58 if rustc < 38 {
59 println!("cargo:rustc-cfg=anyhow_no_macro_reexport");
60 }
61
62 if rustc < 51 {
63 println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
64 }
65 }
66
compile_probe() -> Option<ExitStatus>67 fn compile_probe() -> Option<ExitStatus> {
68 let rustc = env::var_os("RUSTC")?;
69 let out_dir = env::var_os("OUT_DIR")?;
70 let probefile = Path::new(&out_dir).join("probe.rs");
71 fs::write(&probefile, PROBE).ok()?;
72
73 // Make sure to pick up Cargo rustc configuration.
74 let mut cmd = if let Some(wrapper) = env::var_os("CARGO_RUSTC_WRAPPER") {
75 let mut cmd = Command::new(wrapper);
76 // The wrapper's first argument is supposed to be the path to rustc.
77 cmd.arg(rustc);
78 cmd
79 } else {
80 Command::new(rustc)
81 };
82
83 cmd.stderr(Stdio::null())
84 .arg("--edition=2018")
85 .arg("--crate-name=anyhow_build")
86 .arg("--crate-type=lib")
87 .arg("--emit=metadata")
88 .arg("--out-dir")
89 .arg(out_dir)
90 .arg(probefile);
91
92 // If Cargo wants to set RUSTFLAGS, use that.
93 if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
94 if !rustflags.is_empty() {
95 for arg in rustflags.split('\x1f') {
96 cmd.arg(arg);
97 }
98 }
99 }
100
101 cmd.status().ok()
102 }
103
rustc_minor_version() -> Option<u32>104 fn rustc_minor_version() -> Option<u32> {
105 let rustc = env::var_os("RUSTC")?;
106 let output = Command::new(rustc).arg("--version").output().ok()?;
107 let version = str::from_utf8(&output.stdout).ok()?;
108 let mut pieces = version.split('.');
109 if pieces.next() != Some("rustc 1") {
110 return None;
111 }
112 pieces.next()?.parse().ok()
113 }
114