multiversx_sc_meta_lib/tools/
rustc_version.rs1use std::{process::Command, str::FromStr};
2
3use multiversx_sc::abi::RustcAbi;
4use rustc_version::{LlvmVersion, VersionMeta};
5use semver::Version;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct RustcVersion {
10 pub version_meta: VersionMeta,
11 pub short_string: String,
12}
13
14impl RustcVersion {
15 pub fn from_opt_sc_config_serde(opt_serde_version: &Option<String>) -> Self {
17 if let Some(serde_version) = opt_serde_version {
18 Self::from_sc_config_serde(serde_version)
19 } else {
20 Self::current_version()
21 }
22 }
23
24 pub fn from_sc_config_serde(serde_version: &str) -> Self {
25 let version_meta = get_version_meta_for_toolchain(serde_version);
26 RustcVersion {
27 version_meta,
28 short_string: serde_version.to_owned(),
29 }
30 }
31
32 pub fn current_version() -> RustcVersion {
36 let version_meta =
37 rustc_version::version_meta().expect("failed to get rustc version metadata");
38 let short_string = rustc_version_to_string(&version_meta);
39 RustcVersion {
40 version_meta,
41 short_string,
42 }
43 }
44
45 pub fn to_cli_arg(&self) -> String {
47 format!("+{}", self.short_string)
48 }
49
50 pub fn to_abi(&self) -> RustcAbi {
51 RustcAbi {
52 version: self.version_meta.semver.to_string(),
53 commit_hash: self.version_meta.commit_hash.clone().unwrap_or_default(),
54 commit_date: self.version_meta.commit_date.clone().unwrap_or_default(),
55 build_date: self.version_meta.build_date.clone(),
56 channel: format!("{:?}", self.version_meta.channel),
57 host: self.version_meta.host.clone(),
58 short: self.version_meta.short_version_string.clone(),
59 llvm_version: self
60 .version_meta
61 .llvm_version
62 .clone()
63 .map(|llvm_version| llvm_version.to_string()),
64 }
65 }
66
67 pub fn from_abi(abi: &RustcAbi) -> Self {
68 let semver = Version::parse(&abi.version).expect("failed to parse version");
69 let channel = match abi.channel.as_str() {
70 "Stable" => rustc_version::Channel::Stable,
71 "Nightly" => rustc_version::Channel::Nightly,
72 _ => panic!("unsupported channel: {}", abi.channel),
73 };
74
75 RustcVersion {
76 version_meta: VersionMeta {
77 semver,
78 channel,
79 commit_hash: if abi.commit_hash.is_empty() {
80 None
81 } else {
82 Some(abi.commit_hash.clone())
83 },
84 commit_date: if abi.commit_date.is_empty() {
85 None
86 } else {
87 Some(abi.commit_date.clone())
88 },
89 build_date: abi.build_date.clone(),
90 host: abi.host.clone(),
91 short_version_string: abi.short.clone(),
92 llvm_version: abi.llvm_version.clone().map(|llvm_version_string| {
93 LlvmVersion::from_str(&llvm_version_string)
94 .expect("failed to parse LLVM version")
95 }),
96 },
97 short_string: abi.short.clone(),
98 }
99 }
100}
101
102impl std::fmt::Display for RustcVersion {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 write!(f, "{}", self.short_string)
105 }
106}
107
108fn rustc_version_to_string(version_meta: &VersionMeta) -> String {
109 match version_meta.channel {
110 rustc_version::Channel::Stable => format!(
111 "{}-{}",
112 version_to_string(&version_meta.semver),
113 version_meta.host
114 ),
115 rustc_version::Channel::Nightly => {
116 if let Some(build_date) = &version_meta.build_date {
117 format!("nightly-{}-{}", build_date, version_meta.host)
118 } else {
119 "nightly".to_owned()
120 }
121 }
122 _ => panic!("only stable and nightly supported"),
123 }
124}
125
126fn version_to_string(version: &Version) -> String {
128 if version.patch == 0 && version.pre.is_empty() && version.build.is_empty() {
129 format!("{}.{}", version.major, version.minor)
130 } else {
131 version.to_string()
132 }
133}
134
135fn get_version_meta_for_toolchain(toolchain: &str) -> VersionMeta {
137 let output = Command::new("rustc")
139 .arg(format!("+{}", toolchain))
140 .arg("-vV")
141 .output()
142 .expect("failed to call rustc to get full toolchain info");
143
144 if !output.status.success() {
145 panic!(
146 "rustc -vV failed: {}",
147 String::from_utf8_lossy(&output.stderr)
148 );
149 }
150
151 let version_string =
153 String::from_utf8(output.stdout).expect("failed to parse rustc -vV output as UTF-8");
154 rustc_version::version_meta_for(&version_string)
155 .unwrap_or_else(|_| panic!("failed to parse rustc -vV output: {version_string}"))
156}