blob: f9e80a5c8b34e6c69f215502594bc420ffff38a2 [file] [log] [blame]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, single_use_lifetimes)]
use std::{
env, fs,
path::{Path, PathBuf},
process::Command,
str,
};
// The rustc-cfg strings below are *not* public API. Please let us know by
// opening a GitHub issue if your build environment requires some way to enable
// these cfgs other than by executing our build script.
fn main() {
let rustc = env::var_os("RUSTC").map_or_else(|| "rustc".into(), PathBuf::from);
let version = match Version::from_rustc(&rustc) {
Ok(version) => version.print(),
Err(e) => {
println!(
"cargo:warning={}: unable to determine rustc version: {}",
env!("CARGO_PKG_NAME"),
e
);
return;
}
};
let out_dir = env::var_os("OUT_DIR").map(PathBuf::from).expect("OUT_DIR not set");
let out_file = out_dir.join("version.rs");
fs::write(out_file, version).expect("failed to write version.rs");
// Mark as build script has been run successfully.
println!("cargo:rustc-cfg=const_fn_has_build_script");
}
struct Version {
minor: u32,
nightly: bool,
}
impl Version {
// Based on https://github.com/cuviper/autocfg/blob/1.0.1/src/version.rs#L25-L59
//
// TODO: use autocfg if https://github.com/cuviper/autocfg/issues/28 merged
// or https://github.com/taiki-e/const_fn/issues/27 rejected.
fn from_rustc(rustc: &Path) -> Result<Self, String> {
let output =
Command::new(rustc).args(&["--version", "--verbose"]).output().map_err(|e| {
format!("could not execute `{} --version --verbose`: {}", rustc.display(), e)
})?;
if !output.status.success() {
return Err(format!(
"process didn't exit successfully: `{} --version --verbose`",
rustc.display()
));
}
let output = str::from_utf8(&output.stdout).map_err(|e| {
format!("failed to parse output of `{} --version --verbose`: {}", rustc.display(), e)
})?;
// Find the release line in the verbose version output.
let release = output
.lines()
.find(|line| line.starts_with("release: "))
.map(|line| &line["release: ".len()..])
.ok_or_else(|| {
format!(
"could not find rustc release from output of `{} --version --verbose`: {}",
rustc.display(),
output
)
})?;
// Split the version and channel info.
let mut version_channel = release.split('-');
let version = version_channel.next().unwrap();
let channel = version_channel.next();
let minor = (|| {
// Split the version into semver components.
let mut digits = version.splitn(3, '.');
let major = digits.next()?;
if major != "1" {
return None;
}
let minor = digits.next()?.parse().ok()?;
let _patch = digits.next()?;
Some(minor)
})()
.ok_or_else(|| {
format!("unexpected output from `{} --version --verbose`: {}", rustc.display(), output)
})?;
let nightly = channel.map_or(false, |c| c == "dev" || c == "nightly");
Ok(Self { minor, nightly })
}
fn print(&self) -> String {
format!("Version {{ minor: {}, nightly: {} }}\n", self.minor, self.nightly)
}
}