feature_probe/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
#![doc(html_root_url = "https://docs.rs/feature-probe/0.1.1")]
//! To support multiple versions of Rust, it's often necessary to conditionally
//! compile parts of our libraries or programs. It's possible to allow users to
//! specify what features to enable, but detection is better, because users get
//! all the features that their version of Rust supports. And while we could check
//! the rustc version, it's better to probe for individual features. That way,
//! code will work both on nightly, and on stable releases after particular features
//! stabilize, without changes.
//!
//! ## Usage
//!
//! It’s [on crates.io](https://crates.io/crates/feature-probe), so you can add
//!
//! ```toml
//! [build-dependencies]
//! feature-probe = "0.1.1"
//! ```
//!
//! Then add to your `build.rs`:
//!
//! ```no_compile
//! extern crate feature_probe;
//!
//! use feature_probe::Probe;
//! ```
//!
//! Then you can probe for features such as types or expressions. For example:
//!
//! ```no_compile
//! fn main () {
//! let probe = Probe::new();
//!
//! if probe.probe_type("i128") {
//! println!("cargo:rustc-cfg=int_128");
//! }
//!
//! if probe.probe_type("::std::ops::RangeInclusive<u64>") {
//! println!("cargo:rustc-cfg=inclusive_range");
//! }
//! }
//! ```
//!
//! This crate supports Rust version 1.16.0 and later.
use std::env;
use std::ffi::OsString;
use std::io::{self, Write};
use std::process::{Command, Stdio};
/// A probe object, which is used for probing for features.
///
/// Create this with [`ProbeProbeo::new`](#method.new), and then probe with
/// one of the probing methods.
#[derive(Debug)]
pub struct Probe {
rustc: OsString,
out_dir: OsString,
}
impl Probe {
/// Creates a new [`Probe`](struct.Probe.html) object with a default
/// configuration.
///
/// In particular, it consults the environment variable `"RUSTC"` to determine
/// what Rust compiler to use, and the environment variable `"OUT_DIR"` to
/// determine where to put object files. If these are not set, they default to
/// the values `"rustc"` and `"target"`, respectively.
///
/// # Panics
///
/// If the child `rustc` cannot be started or communicated with.
///
/// # Examples
///
/// ```
/// use feature_probe::Probe;
///
/// let probe = Probe::new();
/// assert!( probe.probe_type("u32") );
/// ```
pub fn new() -> Self {
Probe {
rustc: env_var_or("RUSTC", "rustc"),
out_dir: env_var_or("OUT_DIR", "target"),
}
}
/// Probes for the existence of the given type by name.
///
/// # Panics
///
/// If the child `rustc` cannot be started or communicated with.
///
/// # Examples
///
/// ```
/// use feature_probe::Probe;
///
/// let probe = Probe::new();
/// assert!( probe.probe_type("u32") );
/// assert!( ! probe.probe_type("u512") );
/// ```
pub fn probe_type(&self, type_name: &str) -> bool {
self.probe(&format!("pub type T = {}; fn main() {{ }}", type_name))
}
/// Probes whether the given expression can be compiled.
///
/// # Examples
///
/// ```
/// use feature_probe::Probe;
///
/// let probe = Probe::new();
/// assert!( probe.probe_expression("3 + 4") );
/// assert!( ! probe.probe_expression("3 + true") );
pub fn probe_expression(&self, expression: &str) -> bool {
self.probe(&format!("fn main() {{ {}; }}", expression))
}
/// Probes for whether a whole program can be compiled.
///
/// # Panics
///
/// If the child `rustc` cannot be started or communicated with.
///
/// # Examples
///
/// ```
/// # extern crate feature_probe;
/// # fn main() {
/// use feature_probe::Probe;
///
/// let probe = Probe::new();
/// assert!( probe.probe("fn main() { }") );
/// assert!( ! probe.probe("fn main(args: Vec<String>) { }") );
/// # }
/// ```
pub fn probe(&self, code: &str) -> bool {
self.probe_result(code).expect("Probe::probe")
}
/// Probes for whether a whole program can be compiled.
///
/// # Examples
///
/// ```
/// # extern crate feature_probe;
/// # fn main() {
/// use feature_probe::Probe;
///
/// let probe = Probe::new();
/// assert_eq!( probe.probe_result("fn main() { }").unwrap(), true );
/// assert_eq!( probe.probe_result("fn main(args: Vec<String>) { }").unwrap(), false );
/// # }
/// ```
pub fn probe_result(&self, code: &str) -> io::Result<bool> {
let mut child = Command::new(&self.rustc)
.arg("--out-dir")
.arg(&self.out_dir)
.arg("--emit=obj")
.arg("-")
.stdin(Stdio::piped())
.spawn()?;
child
.stdin
.as_mut().unwrap()
.write_all(code.as_bytes())?;
Ok(child.wait()?.success())
}
}
impl Default for Probe {
fn default() -> Self {
Probe::new()
}
}
fn env_var_or(var: &str, default: &str) -> OsString {
env::var_os(var).unwrap_or_else(|| default.into())
}