• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use std::{env, iter, process::Command, str};
4 
rustc_version() -> Option<Version>5 pub(crate) fn rustc_version() -> Option<Version> {
6     let rustc = env::var_os("RUSTC")?;
7     let rustc_wrapper = if env::var_os("CARGO_ENCODED_RUSTFLAGS").is_some() {
8         env::var_os("RUSTC_WRAPPER").filter(|v| !v.is_empty())
9     } else {
10         // Cargo sets environment variables for wrappers correctly only since https://github.com/rust-lang/cargo/pull/9601.
11         None
12     };
13     // Do not apply RUSTC_WORKSPACE_WRAPPER: https://github.com/cuviper/autocfg/issues/58#issuecomment-2067625980
14     let mut rustc = rustc_wrapper.into_iter().chain(iter::once(rustc));
15     let mut cmd = Command::new(rustc.next().unwrap());
16     cmd.args(rustc);
17     // Use verbose version output because the packagers add extra strings to the normal version output.
18     // Do not use long flags (--version --verbose) because clippy-deriver doesn't handle them properly.
19     // -vV is also matched with that cargo internally uses: https://github.com/rust-lang/cargo/blob/0.80.0/src/cargo/util/rustc.rs#L65
20     let output = cmd.arg("-vV").output().ok()?;
21     let verbose_version = str::from_utf8(&output.stdout).ok()?;
22     Version::parse(verbose_version)
23 }
24 
25 #[cfg_attr(test, derive(Debug, PartialEq))]
26 pub(crate) struct Version {
27     pub(crate) minor: u32,
28     pub(crate) nightly: bool,
29     commit_date: Date,
30     pub(crate) llvm: u32,
31 }
32 
33 impl Version {
34     // The known latest stable version. If we unable to determine
35     // the rustc version, we assume this is the current version.
36     // It is no problem if this is older than the actual latest stable.
37     // LLVM version is assumed to be the minimum external LLVM version:
38     // https://github.com/rust-lang/rust/blob/1.81.0/src/bootstrap/src/core/build_steps/llvm.rs#L588
39     pub(crate) const LATEST: Self = Self::stable(81, 17);
40 
stable(rustc_minor: u32, llvm_major: u32) -> Self41     pub(crate) const fn stable(rustc_minor: u32, llvm_major: u32) -> Self {
42         Self { minor: rustc_minor, nightly: false, commit_date: Date::UNKNOWN, llvm: llvm_major }
43     }
44 
probe(&self, minor: u32, year: u16, month: u8, day: u8) -> bool45     pub(crate) fn probe(&self, minor: u32, year: u16, month: u8, day: u8) -> bool {
46         if self.nightly {
47             self.minor > minor
48                 || self.minor == minor && self.commit_date >= Date::new(year, month, day)
49         } else {
50             self.minor >= minor
51         }
52     }
53 
54     #[cfg(test)]
commit_date(&self) -> &Date55     pub(crate) fn commit_date(&self) -> &Date {
56         &self.commit_date
57     }
58 
parse(verbose_version: &str) -> Option<Self>59     pub(crate) fn parse(verbose_version: &str) -> Option<Self> {
60         let mut release = verbose_version
61             .lines()
62             .find(|line| line.starts_with("release: "))
63             .map(|line| &line["release: ".len()..])?
64             .splitn(2, '-');
65         let version = release.next().unwrap();
66         let channel = release.next().unwrap_or_default();
67         let mut digits = version.splitn(3, '.');
68         let major = digits.next()?;
69         if major != "1" {
70             return None;
71         }
72         let minor = digits.next()?.parse::<u32>().ok()?;
73         let _patch = digits.next().unwrap_or("0").parse::<u32>().ok()?;
74         let nightly = channel == "nightly" || channel == "dev";
75 
76         // Note that rustc 1.49-1.50 (and 1.13 or older) don't print LLVM version.
77         let llvm_major = (|| {
78             let version = verbose_version
79                 .lines()
80                 .find(|line| line.starts_with("LLVM version: "))
81                 .map(|line| &line["LLVM version: ".len()..])?;
82             let mut digits = version.splitn(3, '.');
83             let major = digits.next()?.parse::<u32>().ok()?;
84             let _minor = digits.next()?.parse::<u32>().ok()?;
85             let _patch = digits.next().unwrap_or("0").parse::<u32>().ok()?;
86             Some(major)
87         })()
88         .unwrap_or(0);
89 
90         // we don't refer commit date on stable/beta.
91         if nightly {
92             let commit_date = (|| {
93                 let mut commit_date = verbose_version
94                     .lines()
95                     .find(|line| line.starts_with("commit-date: "))
96                     .map(|line| &line["commit-date: ".len()..])?
97                     .splitn(3, '-');
98                 let year = commit_date.next()?.parse::<u16>().ok()?;
99                 let month = commit_date.next()?.parse::<u8>().ok()?;
100                 let day = commit_date.next()?.parse::<u8>().ok()?;
101                 if month > 12 || day > 31 {
102                     return None;
103                 }
104                 Some(Date::new(year, month, day))
105             })();
106             Some(Self {
107                 minor,
108                 nightly,
109                 commit_date: commit_date.unwrap_or(Date::UNKNOWN),
110                 llvm: llvm_major,
111             })
112         } else {
113             Some(Self::stable(minor, llvm_major))
114         }
115     }
116 }
117 
118 #[derive(PartialEq, PartialOrd)]
119 #[cfg_attr(test, derive(Debug))]
120 pub(crate) struct Date {
121     pub(crate) year: u16,
122     pub(crate) month: u8,
123     pub(crate) day: u8,
124 }
125 
126 impl Date {
127     const UNKNOWN: Self = Self::new(0, 0, 0);
128 
new(year: u16, month: u8, day: u8) -> Self129     const fn new(year: u16, month: u8, day: u8) -> Self {
130         Self { year, month, day }
131     }
132 }
133