diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/rustc_version/src | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/rustc_version/src')
-rw-r--r-- | third_party/rust/rustc_version/src/lib.rs | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/third_party/rust/rustc_version/src/lib.rs b/third_party/rust/rustc_version/src/lib.rs new file mode 100644 index 0000000000..cee1ec8143 --- /dev/null +++ b/third_party/rust/rustc_version/src/lib.rs @@ -0,0 +1,417 @@ +// Copyright 2016 rustc-version-rs developers +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![warn(missing_docs)] + +//! Simple library for getting the version information of a `rustc` +//! compiler. +//! +//! This can be used by build scripts or other tools dealing with Rust sources +//! to make decisions based on the version of the compiler. +//! +//! It calls `$RUSTC --version -v` and parses the output, falling +//! back to `rustc` if `$RUSTC` is not set. +//! +//! # Example +//! +//! ```rust +//! // This could be a cargo build script +//! +//! use rustc_version::{version, version_meta, Channel, Version}; +//! +//! // Assert we haven't travelled back in time +//! assert!(version().unwrap().major >= 1); +//! +//! // Set cfg flags depending on release channel +//! match version_meta().unwrap().channel { +//! Channel::Stable => { +//! println!("cargo:rustc-cfg=RUSTC_IS_STABLE"); +//! } +//! Channel::Beta => { +//! println!("cargo:rustc-cfg=RUSTC_IS_BETA"); +//! } +//! Channel::Nightly => { +//! println!("cargo:rustc-cfg=RUSTC_IS_NIGHTLY"); +//! } +//! Channel::Dev => { +//! println!("cargo:rustc-cfg=RUSTC_IS_DEV"); +//! } +//! } +//! +//! // Check for a minimum version +//! if version().unwrap() >= Version::parse("1.4.0").unwrap() { +//! println!("cargo:rustc-cfg=compiler_has_important_bugfix"); +//! } +//! ``` + +#[cfg(test)] +#[macro_use] +extern crate doc_comment; + +#[cfg(test)] +doctest!("../README.md"); + +use std::collections::HashMap; +use std::process::Command; +use std::{env, error, fmt, io, num, str}; +use std::{ffi::OsString, str::FromStr}; + +// Convenience re-export to allow version comparison without needing to add +// semver crate. +pub use semver::Version; + +use Error::*; + +/// Release channel of the compiler. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub enum Channel { + /// Development release channel + Dev, + /// Nightly release channel + Nightly, + /// Beta release channel + Beta, + /// Stable release channel + Stable, +} + +/// LLVM version +/// +/// LLVM's version numbering scheme is not semver compatible until version 4.0 +/// +/// rustc [just prints the major and minor versions], so other parts of the version are not included. +/// +/// [just prints the major and minor versions]: https://github.com/rust-lang/rust/blob/b5c9e2448c9ace53ad5c11585803894651b18b0a/compiler/rustc_codegen_llvm/src/llvm_util.rs#L173-L178 +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct LlvmVersion { + // fields must be ordered major, minor for comparison to be correct + /// Major version + pub major: u64, + /// Minor version + pub minor: u64, + // TODO: expose micro version here +} + +impl fmt::Display for LlvmVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}.{}", self.major, self.minor) + } +} + +impl FromStr for LlvmVersion { + type Err = LlvmVersionParseError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + let mut parts = s + .split('.') + .map(|part| -> Result<u64, LlvmVersionParseError> { + if part == "0" { + Ok(0) + } else if part.starts_with('0') { + Err(LlvmVersionParseError::ComponentMustNotHaveLeadingZeros) + } else if part.starts_with('-') || part.starts_with('+') { + Err(LlvmVersionParseError::ComponentMustNotHaveSign) + } else { + Ok(part.parse()?) + } + }); + + let major = parts.next().unwrap()?; + let mut minor = 0; + + if let Some(part) = parts.next() { + minor = part?; + } else if major < 4 { + // LLVM versions earlier than 4.0 have significant minor versions, so require the minor version in this case. + return Err(LlvmVersionParseError::MinorVersionRequiredBefore4); + } + + if let Some(Err(e)) = parts.next() { + return Err(e); + } + + if parts.next().is_some() { + return Err(LlvmVersionParseError::TooManyComponents); + } + + Ok(Self { major, minor }) + } +} + +/// Rustc version plus metadata like git short hash and build date. +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct VersionMeta { + /// Version of the compiler + pub semver: Version, + + /// Git short hash of the build of the compiler + pub commit_hash: Option<String>, + + /// Commit date of the compiler + pub commit_date: Option<String>, + + /// Build date of the compiler; this was removed between Rust 1.0.0 and 1.1.0. + pub build_date: Option<String>, + + /// Release channel of the compiler + pub channel: Channel, + + /// Host target triple of the compiler + pub host: String, + + /// Short version string of the compiler + pub short_version_string: String, + + /// Version of LLVM used by the compiler + pub llvm_version: Option<LlvmVersion>, +} + +impl VersionMeta { + /// Returns the version metadata for `cmd`, which should be a `rustc` command. + pub fn for_command(mut cmd: Command) -> Result<VersionMeta> { + let out = cmd + .arg("-vV") + .output() + .map_err(Error::CouldNotExecuteCommand)?; + + if !out.status.success() { + return Err(Error::CommandError { + stdout: String::from_utf8_lossy(&out.stdout).into(), + stderr: String::from_utf8_lossy(&out.stderr).into(), + }); + } + + version_meta_for(str::from_utf8(&out.stdout)?) + } +} + +/// Returns the `rustc` SemVer version. +pub fn version() -> Result<Version> { + Ok(version_meta()?.semver) +} + +/// Returns the `rustc` SemVer version and additional metadata +/// like the git short hash and build date. +pub fn version_meta() -> Result<VersionMeta> { + let cmd = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + VersionMeta::for_command(Command::new(cmd)) +} + +/// Parses a "rustc -vV" output string and returns +/// the SemVer version and additional metadata +/// like the git short hash and build date. +pub fn version_meta_for(verbose_version_string: &str) -> Result<VersionMeta> { + let mut map = HashMap::new(); + for (i, line) in verbose_version_string.lines().enumerate() { + if i == 0 { + map.insert("short", line); + continue; + } + + let mut parts = line.splitn(2, ": "); + let key = match parts.next() { + Some(key) => key, + None => continue, + }; + + if let Some(value) = parts.next() { + map.insert(key, value); + } + } + + let short_version_string = expect_key("short", &map)?; + let host = expect_key("host", &map)?; + let release = expect_key("release", &map)?; + let semver: Version = release.parse()?; + + let channel = match semver.pre.split('.').next().unwrap() { + "" => Channel::Stable, + "dev" => Channel::Dev, + "beta" => Channel::Beta, + "nightly" => Channel::Nightly, + x => return Err(Error::UnknownPreReleaseTag(x.to_owned())), + }; + + let commit_hash = expect_key_or_unknown("commit-hash", &map)?; + let commit_date = expect_key_or_unknown("commit-date", &map)?; + let build_date = map + .get("build-date") + .filter(|&v| *v != "unknown") + .map(|&v| String::from(v)); + let llvm_version = match map.get("LLVM version") { + Some(&v) => Some(v.parse()?), + None => None, + }; + + Ok(VersionMeta { + semver, + commit_hash, + commit_date, + build_date, + channel, + host, + short_version_string, + llvm_version, + }) +} + +fn expect_key_or_unknown(key: &str, map: &HashMap<&str, &str>) -> Result<Option<String>, Error> { + match map.get(key) { + Some(&v) if v == "unknown" => Ok(None), + Some(&v) => Ok(Some(String::from(v))), + None => Err(Error::UnexpectedVersionFormat), + } +} + +fn expect_key(key: &str, map: &HashMap<&str, &str>) -> Result<String, Error> { + map.get(key) + .map(|&v| String::from(v)) + .ok_or(Error::UnexpectedVersionFormat) +} + +/// LLVM Version Parse Error +#[derive(Debug)] +pub enum LlvmVersionParseError { + /// An error occurred in parsing a version component as an integer + ParseIntError(num::ParseIntError), + /// A version component must not have leading zeros + ComponentMustNotHaveLeadingZeros, + /// A version component has a sign + ComponentMustNotHaveSign, + /// Minor version component must be zero on LLVM versions later than 4.0 + MinorVersionMustBeZeroAfter4, + /// Minor version component is required on LLVM versions earlier than 4.0 + MinorVersionRequiredBefore4, + /// Too many components + TooManyComponents, +} + +impl From<num::ParseIntError> for LlvmVersionParseError { + fn from(e: num::ParseIntError) -> Self { + LlvmVersionParseError::ParseIntError(e) + } +} + +impl fmt::Display for LlvmVersionParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LlvmVersionParseError::ParseIntError(e) => { + write!(f, "error parsing LLVM version component: {}", e) + } + LlvmVersionParseError::ComponentMustNotHaveLeadingZeros => { + write!(f, "a version component must not have leading zeros") + } + LlvmVersionParseError::ComponentMustNotHaveSign => { + write!(f, "a version component must not have a sign") + } + LlvmVersionParseError::MinorVersionMustBeZeroAfter4 => write!( + f, + "LLVM's minor version component must be 0 for versions greater than 4.0" + ), + LlvmVersionParseError::MinorVersionRequiredBefore4 => write!( + f, + "LLVM's minor version component is required for versions less than 4.0" + ), + LlvmVersionParseError::TooManyComponents => write!(f, "too many version components"), + } + } +} + +impl error::Error for LlvmVersionParseError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + LlvmVersionParseError::ParseIntError(e) => Some(e), + LlvmVersionParseError::ComponentMustNotHaveLeadingZeros + | LlvmVersionParseError::ComponentMustNotHaveSign + | LlvmVersionParseError::MinorVersionMustBeZeroAfter4 + | LlvmVersionParseError::MinorVersionRequiredBefore4 + | LlvmVersionParseError::TooManyComponents => None, + } + } +} + +/// The error type for this crate. +#[derive(Debug)] +pub enum Error { + /// An error occurred while trying to find the `rustc` to run. + CouldNotExecuteCommand(io::Error), + /// Error output from the command that was run. + CommandError { + /// stdout output from the command + stdout: String, + /// stderr output from the command + stderr: String, + }, + /// The output of `rustc -vV` was not valid utf-8. + Utf8Error(str::Utf8Error), + /// The output of `rustc -vV` was not in the expected format. + UnexpectedVersionFormat, + /// An error occurred in parsing the semver. + SemVerError(semver::Error), + /// The pre-release tag is unknown. + UnknownPreReleaseTag(String), + /// An error occurred in parsing a `LlvmVersion`. + LlvmVersionError(LlvmVersionParseError), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + CouldNotExecuteCommand(ref e) => write!(f, "could not execute command: {}", e), + CommandError { + ref stdout, + ref stderr, + } => write!( + f, + "error from command -- stderr:\n\n{}\n\nstderr:\n\n{}", + stderr, stdout, + ), + Utf8Error(_) => write!(f, "invalid UTF-8 output from `rustc -vV`"), + UnexpectedVersionFormat => write!(f, "unexpected `rustc -vV` format"), + SemVerError(ref e) => write!(f, "error parsing version: {}", e), + UnknownPreReleaseTag(ref i) => write!(f, "unknown pre-release tag: {}", i), + LlvmVersionError(ref e) => write!(f, "error parsing LLVM's version: {}", e), + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + CouldNotExecuteCommand(ref e) => Some(e), + CommandError { .. } => None, + Utf8Error(ref e) => Some(e), + UnexpectedVersionFormat => None, + SemVerError(ref e) => Some(e), + UnknownPreReleaseTag(_) => None, + LlvmVersionError(ref e) => Some(e), + } + } +} + +macro_rules! impl_from { + ($($err_ty:ty => $variant:ident),* $(,)*) => { + $( + impl From<$err_ty> for Error { + fn from(e: $err_ty) -> Error { + Error::$variant(e) + } + } + )* + } +} + +impl_from! { + str::Utf8Error => Utf8Error, + semver::Error => SemVerError, + LlvmVersionParseError => LlvmVersionError, +} + +/// The result type for this crate. +pub type Result<T, E = Error> = std::result::Result<T, E>; |