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(error_generic_member_access, provide_any)]
19
20 use std::any::{Demand, Provider};
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 backtrace: Backtrace,
28 }
29
30 impl Display for E {
31 fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
32 unimplemented!()
33 }
34 }
35
36 impl Error for E {
37 fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
38 demand.provide_ref(&self.backtrace);
39 }
40 }
41
42 struct P;
43
44 impl Provider for P {
45 fn provide<'a>(&'a self, _demand: &mut Demand<'a>) {}
46 }
47
48 const _: fn() = || {
49 let backtrace: Backtrace = Backtrace::capture();
50 let status: BacktraceStatus = backtrace.status();
51 match status {
52 BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
53 }
54 };
55
56 const _: fn(&dyn Error) -> Option<&Backtrace> = |err| err.request_ref::<Backtrace>();
57 "#;
58
main()59 fn main() {
60 if cfg!(feature = "std") {
61 match compile_probe() {
62 Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
63 _ => {}
64 }
65 }
66
67 let rustc = match rustc_minor_version() {
68 Some(rustc) => rustc,
69 None => return,
70 };
71
72 if rustc < 51 {
73 println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
74 }
75
76 if rustc < 52 {
77 println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str");
78 }
79 }
80
compile_probe() -> Option<ExitStatus>81 fn compile_probe() -> Option<ExitStatus> {
82 let rustc = env::var_os("RUSTC")?;
83 let out_dir = env::var_os("OUT_DIR")?;
84 let probefile = Path::new(&out_dir).join("probe.rs");
85 fs::write(&probefile, PROBE).ok()?;
86
87 // Make sure to pick up Cargo rustc configuration.
88 let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") {
89 let mut cmd = Command::new(wrapper);
90 // The wrapper's first argument is supposed to be the path to rustc.
91 cmd.arg(rustc);
92 cmd
93 } else {
94 Command::new(rustc)
95 };
96
97 cmd.stderr(Stdio::null())
98 .arg("--edition=2018")
99 .arg("--crate-name=anyhow_build")
100 .arg("--crate-type=lib")
101 .arg("--emit=metadata")
102 .arg("--out-dir")
103 .arg(out_dir)
104 .arg(probefile);
105
106 if let Some(target) = env::var_os("TARGET") {
107 cmd.arg("--target").arg(target);
108 }
109
110 // If Cargo wants to set RUSTFLAGS, use that.
111 if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
112 if !rustflags.is_empty() {
113 for arg in rustflags.split('\x1f') {
114 cmd.arg(arg);
115 }
116 }
117 }
118
119 cmd.status().ok()
120 }
121
rustc_minor_version() -> Option<u32>122 fn rustc_minor_version() -> Option<u32> {
123 let rustc = env::var_os("RUSTC")?;
124 let output = Command::new(rustc).arg("--version").output().ok()?;
125 let version = str::from_utf8(&output.stdout).ok()?;
126 let mut pieces = version.split('.');
127 if pieces.next() != Some("rustc 1") {
128 return None;
129 }
130 pieces.next()?.parse().ok()
131 }
132