Skip to content

Commit c5eadf7

Browse files
authored
Merge pull request #1004 from rust-lang/version-report
Migrate version reporting to the orchestrator
2 parents 047e5b7 + 235ffe9 commit c5eadf7

File tree

6 files changed

+402
-448
lines changed

6 files changed

+402
-448
lines changed

compiler/base/orchestrator/src/coordinator.rs

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,134 @@ use crate::{
3535
DropErrorDetailsExt,
3636
};
3737

38+
#[derive(Debug, Clone, PartialEq, Eq)]
39+
pub struct Versions {
40+
pub stable: ChannelVersions,
41+
pub beta: ChannelVersions,
42+
pub nightly: ChannelVersions,
43+
}
44+
45+
#[derive(Debug, Clone, PartialEq, Eq)]
46+
pub struct ChannelVersions {
47+
pub rustc: Version,
48+
pub rustfmt: Version,
49+
pub clippy: Version,
50+
pub miri: Option<Version>,
51+
}
52+
53+
/// Parsing this struct is very lenient — we'd rather return some
54+
/// partial data instead of absolutely nothing.
55+
#[derive(Debug, Clone, PartialEq, Eq)]
56+
pub struct Version {
57+
pub release: String,
58+
pub commit_hash: String,
59+
pub commit_date: String,
60+
}
61+
62+
impl Version {
63+
fn parse_rustc_version_verbose(rustc_version: &str) -> Self {
64+
let mut release = "";
65+
let mut commit_hash = "";
66+
let mut commit_date = "";
67+
68+
let fields = rustc_version.lines().skip(1).filter_map(|line| {
69+
let mut pieces = line.splitn(2, ':');
70+
let key = pieces.next()?.trim();
71+
let value = pieces.next()?.trim();
72+
Some((key, value))
73+
});
74+
75+
for (k, v) in fields {
76+
match k {
77+
"release" => release = v,
78+
"commit-hash" => commit_hash = v,
79+
"commit-date" => commit_date = v,
80+
_ => {}
81+
}
82+
}
83+
84+
Self {
85+
release: release.into(),
86+
commit_hash: commit_hash.into(),
87+
commit_date: commit_date.into(),
88+
}
89+
}
90+
91+
// Parses versions of the shape `toolname 0.0.0 (0000000 0000-00-00)`
92+
fn parse_tool_version(tool_version: &str) -> Self {
93+
let mut parts = tool_version.split_whitespace().fuse().skip(1);
94+
95+
let release = parts.next().unwrap_or("").into();
96+
let commit_hash = parts.next().unwrap_or("").trim_start_matches('(').into();
97+
let commit_date = parts.next().unwrap_or("").trim_end_matches(')').into();
98+
99+
Self {
100+
release,
101+
commit_hash,
102+
commit_date,
103+
}
104+
}
105+
}
106+
107+
#[derive(Debug, Snafu)]
108+
#[snafu(module)]
109+
pub enum VersionsError {
110+
#[snafu(display("Unable to determine versions for the stable channel"))]
111+
Stable { source: VersionsChannelError },
112+
113+
#[snafu(display("Unable to determine versions for the beta channel"))]
114+
Beta { source: VersionsChannelError },
115+
116+
#[snafu(display("Unable to determine versions for the nightly channel"))]
117+
Nightly { source: VersionsChannelError },
118+
}
119+
120+
#[derive(Debug, Snafu)]
121+
pub enum VersionsChannelError {
122+
#[snafu(context(false))] // transparent
123+
Channel { source: Error },
124+
125+
#[snafu(context(false))] // transparent
126+
Versions { source: ContainerVersionsError },
127+
}
128+
129+
#[derive(Debug, Snafu)]
130+
#[snafu(module)]
131+
pub enum ContainerVersionsError {
132+
#[snafu(display("Failed to get `rustc` version"))]
133+
Rustc { source: VersionError },
134+
135+
#[snafu(display("`rustc` not executable"))]
136+
RustcMissing,
137+
138+
#[snafu(display("Failed to get `rustfmt` version"))]
139+
Rustfmt { source: VersionError },
140+
141+
#[snafu(display("`cargo fmt` not executable"))]
142+
RustfmtMissing,
143+
144+
#[snafu(display("Failed to get clippy version"))]
145+
Clippy { source: VersionError },
146+
147+
#[snafu(display("`cargo clippy` not executable"))]
148+
ClippyMissing,
149+
150+
#[snafu(display("Failed to get miri version"))]
151+
Miri { source: VersionError },
152+
}
153+
154+
#[derive(Debug, Snafu)]
155+
#[snafu(module)]
156+
pub enum VersionError {
157+
#[snafu(display("Could not start the process"))]
158+
#[snafu(context(false))]
159+
SpawnProcess { source: SpawnCargoError },
160+
161+
#[snafu(display("The task panicked"))]
162+
#[snafu(context(false))]
163+
TaskPanic { source: tokio::task::JoinError },
164+
}
165+
38166
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39167
pub enum AssemblyFlavor {
40168
Att,
@@ -639,6 +767,28 @@ where
639767
}
640768
}
641769

770+
pub async fn versions(&self) -> Result<Versions, VersionsError> {
771+
use versions_error::*;
772+
773+
let [stable, beta, nightly] =
774+
[Channel::Stable, Channel::Beta, Channel::Nightly].map(|c| async move {
775+
let c = self.select_channel(c).await?;
776+
c.versions().await.map_err(VersionsChannelError::from)
777+
});
778+
779+
let (stable, beta, nightly) = join!(stable, beta, nightly);
780+
781+
let stable = stable.context(StableSnafu)?;
782+
let beta = beta.context(BetaSnafu)?;
783+
let nightly = nightly.context(NightlySnafu)?;
784+
785+
Ok(Versions {
786+
stable,
787+
beta,
788+
nightly,
789+
})
790+
}
791+
642792
pub async fn execute(
643793
&self,
644794
request: ExecuteRequest,
@@ -904,6 +1054,72 @@ impl Container {
9041054
})
9051055
}
9061056

1057+
async fn versions(&self) -> Result<ChannelVersions, ContainerVersionsError> {
1058+
use container_versions_error::*;
1059+
1060+
let token = CancellationToken::new();
1061+
1062+
let rustc = self.rustc_version(token.clone());
1063+
let rustfmt = self.tool_version(token.clone(), "fmt");
1064+
let clippy = self.tool_version(token.clone(), "clippy");
1065+
let miri = self.tool_version(token, "miri");
1066+
1067+
let (rustc, rustfmt, clippy, miri) = join!(rustc, rustfmt, clippy, miri);
1068+
1069+
let rustc = rustc.context(RustcSnafu)?.context(RustcMissingSnafu)?;
1070+
let rustfmt = rustfmt
1071+
.context(RustfmtSnafu)?
1072+
.context(RustfmtMissingSnafu)?;
1073+
let clippy = clippy.context(ClippySnafu)?.context(ClippyMissingSnafu)?;
1074+
let miri = miri.context(MiriSnafu)?;
1075+
1076+
Ok(ChannelVersions {
1077+
rustc,
1078+
rustfmt,
1079+
clippy,
1080+
miri,
1081+
})
1082+
}
1083+
1084+
async fn rustc_version(
1085+
&self,
1086+
token: CancellationToken,
1087+
) -> Result<Option<Version>, VersionError> {
1088+
let rustc_cmd = ExecuteCommandRequest::simple("rustc", ["--version", "--verbose"]);
1089+
let output = self.version_output(token, rustc_cmd).await?;
1090+
1091+
Ok(output.map(|o| Version::parse_rustc_version_verbose(&o)))
1092+
}
1093+
1094+
async fn tool_version(
1095+
&self,
1096+
token: CancellationToken,
1097+
subcommand_name: &str,
1098+
) -> Result<Option<Version>, VersionError> {
1099+
let tool_cmd = ExecuteCommandRequest::simple("cargo", [subcommand_name, "--version"]);
1100+
let output = self.version_output(token, tool_cmd).await?;
1101+
1102+
Ok(output.map(|o| Version::parse_tool_version(&o)))
1103+
}
1104+
1105+
async fn version_output(
1106+
&self,
1107+
token: CancellationToken,
1108+
cmd: ExecuteCommandRequest,
1109+
) -> Result<Option<String>, VersionError> {
1110+
let v = self.spawn_cargo_task(token.clone(), cmd).await?;
1111+
let SpawnCargo {
1112+
task,
1113+
stdin_tx,
1114+
stdout_rx,
1115+
stderr_rx,
1116+
} = v;
1117+
drop(stdin_tx);
1118+
let task = async { task.await?.map_err(VersionError::from) };
1119+
let o = WithOutput::try_absorb(task, stdout_rx, stderr_rx).await?;
1120+
Ok(if o.success { Some(o.stdout) } else { None })
1121+
}
1122+
9071123
async fn execute(
9081124
&self,
9091125
request: ExecuteRequest,
@@ -2416,6 +2632,20 @@ mod tests {
24162632
}
24172633
}
24182634

2635+
#[tokio::test]
2636+
#[snafu::report]
2637+
async fn versions() -> Result<()> {
2638+
let coordinator = new_coordinator().await;
2639+
2640+
let versions = coordinator.versions().with_timeout().await.unwrap();
2641+
2642+
assert_starts_with!(versions.stable.rustc.release, "1.");
2643+
2644+
coordinator.shutdown().await?;
2645+
2646+
Ok(())
2647+
}
2648+
24192649
const ARBITRARY_EXECUTE_REQUEST: ExecuteRequest = ExecuteRequest {
24202650
channel: Channel::Stable,
24212651
mode: Mode::Debug,

compiler/base/orchestrator/src/message.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,20 @@ pub struct ExecuteCommandRequest {
116116
pub cwd: Option<String>, // None means in project direcotry.
117117
}
118118

119+
impl ExecuteCommandRequest {
120+
pub fn simple(
121+
cmd: impl Into<String>,
122+
args: impl IntoIterator<Item = impl Into<String>>,
123+
) -> Self {
124+
Self {
125+
cmd: cmd.into(),
126+
args: args.into_iter().map(Into::into).collect(),
127+
envs: Default::default(),
128+
cwd: None,
129+
}
130+
}
131+
}
132+
119133
#[derive(Debug, Serialize, Deserialize)]
120134
pub struct ExecuteCommandResponse {
121135
pub success: bool,

ui/src/main.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,14 @@ enum Error {
219219
#[snafu(display("The WebSocket worker panicked: {}", text))]
220220
WebSocketTaskPanic { text: String },
221221

222+
#[snafu(display("Unable to find the available versions"))]
223+
Versions {
224+
source: orchestrator::coordinator::VersionsError,
225+
},
226+
227+
#[snafu(display("The Miri version was missing"))]
228+
MiriVersion,
229+
222230
#[snafu(display("Unable to shutdown the coordinator"))]
223231
ShutdownCoordinator {
224232
source: orchestrator::coordinator::Error,
@@ -471,16 +479,6 @@ impl From<Vec<sandbox::CrateInformation>> for MetaCratesResponse {
471479
}
472480
}
473481

474-
impl From<sandbox::Version> for MetaVersionResponse {
475-
fn from(me: sandbox::Version) -> Self {
476-
MetaVersionResponse {
477-
version: me.release.into(),
478-
hash: me.commit_hash.into(),
479-
date: me.commit_date.into(),
480-
}
481-
}
482-
}
483-
484482
impl From<gist::Gist> for MetaGistResponse {
485483
fn from(me: gist::Gist) -> Self {
486484
MetaGistResponse {

0 commit comments

Comments
 (0)