-
-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathrustup.rs
179 lines (159 loc) · 5.38 KB
/
rustup.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: Apache-2.0 OR MIT
use std::str;
use anyhow::{Result, bail, format_err};
use crate::{
LogGroup, PackageRuns, cargo,
context::Context,
version::{MaybeVersion, Version, VersionRange},
};
pub(crate) struct Rustup {
pub(crate) version: u32,
}
impl Rustup {
pub(crate) fn new() -> Self {
// If failed to determine rustup version, assume the latest version.
let version = minor_version()
.map_err(|e| {
warn!("unable to determine rustup version; assuming latest stable rustup: {e:#}");
})
.unwrap_or(u32::MAX);
Self { version }
}
}
pub(crate) fn version_range(
range: VersionRange,
step: u16,
packages: &[PackageRuns<'_>],
cx: &Context,
) -> Result<Vec<Version>> {
let check = |version: &Version| {
if version.major != 1 {
bail!("major version must be 1");
}
if let Some(patch) = version.patch {
warn!(
"--version-range always selects the latest patch release per minor release, \
not the specified patch release `{patch}`",
);
}
Ok(())
};
let mut stable_version = None;
let mut get_stable_version = || -> Result<Version> {
if let Some(stable_version) = stable_version {
Ok(stable_version)
} else {
let print_output = false;
install_toolchain("stable", &[], print_output, LogGroup::None)?;
let version = cargo::version(cmd!("rustup", "run", "stable", "cargo"))?;
stable_version = Some(version);
Ok(version)
}
};
let mut rust_version = None;
let mut get_rust_version = || -> Result<Version> {
if let Some(rust_version) = rust_version {
Ok(rust_version)
} else {
let mut lowest_msrv = None;
for pkg in packages {
let pkg_msrv = cx
.rust_version(pkg.id)
.map(str::parse::<Version>)
.transpose()?
.map(Version::strip_patch);
lowest_msrv = match (lowest_msrv, pkg_msrv) {
(Some(workspace), Some(pkg)) => {
if workspace < pkg {
Some(workspace)
} else {
Some(pkg)
}
}
(Some(msrv), None) | (None, Some(msrv)) => Some(msrv),
(None, None) => None,
};
}
let Some(lowest_msrv) = lowest_msrv else {
bail!("no rust-version field in selected Cargo.toml's is specified")
};
rust_version = Some(lowest_msrv);
Ok(lowest_msrv)
}
};
let VersionRange { start_inclusive, end_inclusive } = range;
let start_inclusive = match start_inclusive {
MaybeVersion::Version(start) => {
check(&start)?;
start
}
MaybeVersion::Msrv => {
let start = get_rust_version()?;
check(&start)?;
start
}
MaybeVersion::Stable => get_stable_version()?,
};
let end_inclusive = match end_inclusive {
MaybeVersion::Version(end) => {
check(&end)?;
end
}
MaybeVersion::Msrv => get_rust_version()?,
MaybeVersion::Stable => get_stable_version()?,
};
let versions: Vec<_> = (start_inclusive.minor..=end_inclusive.minor)
.step_by(step as usize)
.map(|minor| Version { major: 1, minor, patch: None })
.collect();
if versions.is_empty() {
bail!("specified version range `{range}` is empty");
}
Ok(versions)
}
pub(crate) fn install_toolchain(
mut toolchain: &str,
target: &[String],
print_output: bool,
log_group: LogGroup,
) -> Result<()> {
toolchain = toolchain.strip_prefix('+').unwrap_or(toolchain);
if target.is_empty()
&& cmd!("rustup", "run", toolchain, "cargo", "--version").run_with_output().is_ok()
{
// Do not run `rustup toolchain add` if the toolchain already has installed.
return Ok(());
}
// In Github Actions and Azure Pipelines, --no-self-update is necessary
// because the windows environment cannot self-update rustup.exe.
let mut cmd = cmd!("rustup", "toolchain", "add", toolchain, "--no-self-update");
if !target.is_empty() {
cmd.args(["--target", &target.join(",")]);
}
if print_output {
let _guard = log_group.print(&format!("running {cmd}"));
// The toolchain installation can take some time, so we'll show users
// the progress.
cmd.run()
} else {
// However, in certain situations, it may be preferable not to display it.
cmd.run_with_output().map(drop)
}
}
fn minor_version() -> Result<u32> {
let cmd = cmd!("rustup", "--version");
let output = cmd.read()?;
let version = (|| {
let mut output = output.split(' ');
if output.next()? != "rustup" {
return None;
}
output.next()
})()
.ok_or_else(|| format_err!("unexpected output from {cmd}: {output}"))?;
let version: Version = version.parse()?;
if version.major != 1 || version.patch.is_none() {
bail!("unexpected output from {cmd}: {output}");
}
Ok(version.minor)
}