| // SPDX-License-Identifier: Apache-2.0 OR MIT |
| |
| use std::{env, iter, process::Command, str}; |
| |
| pub(crate) fn rustc_version() -> Option<Version> { |
| let rustc = env::var_os("RUSTC")?; |
| let rustc_wrapper = if env::var_os("CARGO_ENCODED_RUSTFLAGS").is_some() { |
| env::var_os("RUSTC_WRAPPER").filter(|v| !v.is_empty()) |
| } else { |
| // Cargo sets environment variables for wrappers correctly only since https://github.com/rust-lang/cargo/pull/9601. |
| None |
| }; |
| // Do not apply RUSTC_WORKSPACE_WRAPPER: https://github.com/cuviper/autocfg/issues/58#issuecomment-2067625980 |
| let mut rustc = rustc_wrapper.into_iter().chain(iter::once(rustc)); |
| let mut cmd = Command::new(rustc.next().unwrap()); |
| cmd.args(rustc); |
| // Use verbose version output because the packagers add extra strings to the normal version output. |
| // Do not use long flags (--version --verbose) because clippy-deriver doesn't handle them properly. |
| // -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 |
| let output = cmd.arg("-vV").output().ok()?; |
| let verbose_version = str::from_utf8(&output.stdout).ok()?; |
| Version::parse(verbose_version) |
| } |
| |
| #[cfg_attr(test, derive(Debug, PartialEq))] |
| pub(crate) struct Version { |
| pub(crate) minor: u32, |
| pub(crate) nightly: bool, |
| commit_date: Date, |
| pub(crate) llvm: u32, |
| } |
| |
| impl Version { |
| // The known latest stable version. If we unable to determine |
| // the rustc version, we assume this is the current version. |
| // It is no problem if this is older than the actual latest stable. |
| // LLVM version is assumed to be the minimum external LLVM version: |
| // https://github.com/rust-lang/rust/blob/1.81.0/src/bootstrap/src/core/build_steps/llvm.rs#L588 |
| pub(crate) const LATEST: Self = Self::stable(81, 17); |
| |
| pub(crate) const fn stable(rustc_minor: u32, llvm_major: u32) -> Self { |
| Self { minor: rustc_minor, nightly: false, commit_date: Date::UNKNOWN, llvm: llvm_major } |
| } |
| |
| pub(crate) fn probe(&self, minor: u32, year: u16, month: u8, day: u8) -> bool { |
| if self.nightly { |
| self.minor > minor |
| || self.minor == minor && self.commit_date >= Date::new(year, month, day) |
| } else { |
| self.minor >= minor |
| } |
| } |
| |
| #[cfg(test)] |
| pub(crate) fn commit_date(&self) -> &Date { |
| &self.commit_date |
| } |
| |
| pub(crate) fn parse(verbose_version: &str) -> Option<Self> { |
| let mut release = verbose_version |
| .lines() |
| .find(|line| line.starts_with("release: ")) |
| .map(|line| &line["release: ".len()..])? |
| .splitn(2, '-'); |
| let version = release.next().unwrap(); |
| let channel = release.next().unwrap_or_default(); |
| let mut digits = version.splitn(3, '.'); |
| let major = digits.next()?; |
| if major != "1" { |
| return None; |
| } |
| let minor = digits.next()?.parse::<u32>().ok()?; |
| let _patch = digits.next().unwrap_or("0").parse::<u32>().ok()?; |
| let nightly = channel == "nightly" || channel == "dev"; |
| |
| // Note that rustc 1.49-1.50 (and 1.13 or older) don't print LLVM version. |
| let llvm_major = (|| { |
| let version = verbose_version |
| .lines() |
| .find(|line| line.starts_with("LLVM version: ")) |
| .map(|line| &line["LLVM version: ".len()..])?; |
| let mut digits = version.splitn(3, '.'); |
| let major = digits.next()?.parse::<u32>().ok()?; |
| let _minor = digits.next()?.parse::<u32>().ok()?; |
| let _patch = digits.next().unwrap_or("0").parse::<u32>().ok()?; |
| Some(major) |
| })() |
| .unwrap_or(0); |
| |
| // we don't refer commit date on stable/beta. |
| if nightly { |
| let commit_date = (|| { |
| let mut commit_date = verbose_version |
| .lines() |
| .find(|line| line.starts_with("commit-date: ")) |
| .map(|line| &line["commit-date: ".len()..])? |
| .splitn(3, '-'); |
| let year = commit_date.next()?.parse::<u16>().ok()?; |
| let month = commit_date.next()?.parse::<u8>().ok()?; |
| let day = commit_date.next()?.parse::<u8>().ok()?; |
| if month > 12 || day > 31 { |
| return None; |
| } |
| Some(Date::new(year, month, day)) |
| })(); |
| Some(Self { |
| minor, |
| nightly, |
| commit_date: commit_date.unwrap_or(Date::UNKNOWN), |
| llvm: llvm_major, |
| }) |
| } else { |
| Some(Self::stable(minor, llvm_major)) |
| } |
| } |
| } |
| |
| #[derive(PartialEq, PartialOrd)] |
| #[cfg_attr(test, derive(Debug))] |
| pub(crate) struct Date { |
| pub(crate) year: u16, |
| pub(crate) month: u8, |
| pub(crate) day: u8, |
| } |
| |
| impl Date { |
| const UNKNOWN: Self = Self::new(0, 0, 0); |
| |
| const fn new(year: u16, month: u8, day: u8) -> Self { |
| Self { year, month, day } |
| } |
| } |