]>
Commit | Line | Data |
---|---|---|
5869c6ff XL |
1 | #![forbid(unsafe_code)] |
2 | #![warn(rust_2018_idioms, single_use_lifetimes)] | |
3 | ||
4 | use std::{ | |
5 | env, fs, | |
6 | path::{Path, PathBuf}, | |
7 | process::Command, | |
8 | str, | |
9 | }; | |
10 | ||
11 | // The rustc-cfg strings below are *not* public API. Please let us know by | |
12 | // opening a GitHub issue if your build environment requires some way to enable | |
13 | // these cfgs other than by executing our build script. | |
14 | fn main() { | |
15 | let rustc: PathBuf = env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()).into(); | |
16 | let version = match Version::from_rustc(&rustc) { | |
17 | Ok(version) => version.print(), | |
18 | Err(e) => { | |
19 | println!( | |
20 | "cargo:warning={}: unable to determine rustc version: {}", | |
21 | env!("CARGO_PKG_NAME"), | |
22 | e | |
23 | ); | |
24 | return; | |
25 | } | |
26 | }; | |
27 | ||
28 | let out_dir: PathBuf = env::var_os("OUT_DIR").expect("OUT_DIR not set").into(); | |
29 | let out_file = &out_dir.join("version"); | |
30 | fs::write(out_file, version) | |
31 | .unwrap_or_else(|e| panic!("failed to write {}: {}", out_file.display(), e)); | |
32 | ||
33 | // Mark as build script has been run successfully. | |
34 | println!("cargo:rustc-cfg=const_fn_has_build_script"); | |
35 | } | |
36 | ||
37 | struct Version { | |
38 | minor: u32, | |
39 | nightly: bool, | |
40 | } | |
41 | ||
42 | impl Version { | |
43 | // Based on https://github.com/cuviper/autocfg/blob/1.0.1/src/version.rs#L25-L59 | |
44 | // | |
45 | // TODO: use autocfg if https://github.com/cuviper/autocfg/issues/28 merged | |
46 | // or https://github.com/taiki-e/const_fn/issues/27 rejected. | |
47 | fn from_rustc(rustc: &Path) -> Result<Self, String> { | |
48 | let output = | |
49 | Command::new(rustc).args(&["--version", "--verbose"]).output().map_err(|e| { | |
50 | format!("could not execute `{} --version --verbose`: {}", rustc.display(), e) | |
51 | })?; | |
52 | if !output.status.success() { | |
53 | return Err(format!( | |
54 | "process didn't exit successfully: `{} --version --verbose`", | |
55 | rustc.display() | |
56 | )); | |
57 | } | |
58 | let output = str::from_utf8(&output.stdout).map_err(|e| { | |
59 | format!("failed to parse output of `{} --version --verbose`: {}", rustc.display(), e) | |
60 | })?; | |
61 | ||
62 | // Find the release line in the verbose version output. | |
63 | let release = output | |
64 | .lines() | |
65 | .find(|line| line.starts_with("release: ")) | |
66 | .map(|line| &line["release: ".len()..]) | |
67 | .ok_or_else(|| { | |
68 | format!( | |
69 | "could not find rustc release from output of `{} --version --verbose`: {}", | |
70 | rustc.display(), | |
71 | output | |
72 | ) | |
73 | })?; | |
74 | ||
75 | // Split the version and channel info. | |
76 | let mut version_channel = release.split('-'); | |
77 | let version = version_channel.next().unwrap(); | |
78 | let channel = version_channel.next(); | |
79 | ||
80 | let minor = (|| { | |
81 | // Split the version into semver components. | |
82 | let mut digits = version.splitn(3, '.'); | |
83 | let major = digits.next()?; | |
84 | if major != "1" { | |
85 | return None; | |
86 | } | |
87 | let minor = digits.next()?.parse().ok()?; | |
88 | let _patch = digits.next()?; | |
89 | Some(minor) | |
90 | })() | |
91 | .ok_or_else(|| { | |
92 | format!("unexpected output from `{} --version --verbose`: {}", rustc.display(), output) | |
93 | })?; | |
94 | ||
95 | let nightly = channel.map_or(false, |c| c == "dev" || c == "nightly"); | |
96 | Ok(Self { minor, nightly }) | |
97 | } | |
98 | ||
99 | fn print(&self) -> String { | |
100 | format!("Version {{ minor: {}, nightly: {} }}\n", self.minor, self.nightly) | |
101 | } | |
102 | } |