• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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