Skip to content

Commit 1cb6b77

Browse files
committed
Add Archive of Rust Stable Standalone Installers
1 parent 55bacb0 commit 1cb6b77

File tree

4 files changed

+220
-3
lines changed

4 files changed

+220
-3
lines changed

blacksmith/src/channel.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use std::collections::BTreeMap;
21
use serde::Deserialize;
2+
use std::collections::BTreeMap;
33

44
#[derive(Deserialize)]
55
pub struct Target {
@@ -21,4 +21,3 @@ pub struct Packages {
2121
pub struct Channel {
2222
pub pkg: Packages,
2323
}
24-

blacksmith/src/lib.rs

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub struct Blacksmith {
4545
rustup: Vec<String>,
4646
stable_version: Option<String>,
4747
platforms: BTreeMap<String, Platform>,
48+
previous_stable_versions: Vec<(String, Vec<String>)>,
4849
}
4950

5051
impl Blacksmith {
@@ -123,6 +124,63 @@ impl Blacksmith {
123124
}
124125
}
125126

127+
let mut stable_version = SemVersion::from_str(&blacksmith.stable_version.clone().unwrap());
128+
129+
// Go backwards in stable versions
130+
while let Some(mut previous_stable_version) = stable_version.previous() {
131+
let channel_url = if previous_stable_version.patch.is_none() {
132+
// Download https://static.rust-lang.org/dist/channel-rust-{major.minor}.toml
133+
// and see if we had patch versions of it.
134+
format!(
135+
"{}{}.{}.toml",
136+
CHANNEL_URL_PREFIX,
137+
previous_stable_version.major,
138+
previous_stable_version.minor
139+
)
140+
} else {
141+
// Download https://static.rust-lang.org/dist/channel-rust-{major.minor.patch}.toml and process it.
142+
format!(
143+
"{}{}.{}.{}.toml",
144+
CHANNEL_URL_PREFIX,
145+
previous_stable_version.major,
146+
previous_stable_version.minor,
147+
previous_stable_version.patch.unwrap()
148+
)
149+
};
150+
151+
let content = reqwest::blocking::get(&channel_url)?.text()?;
152+
let rust = toml::from_str::<crate::channel::Channel>(&content)?
153+
.pkg
154+
.rust;
155+
156+
log::info!(
157+
"Found {} targets for stable v{}",
158+
rust.target.len(),
159+
rust.version
160+
);
161+
162+
let version = rust.version.split(' ').next().unwrap().to_string();
163+
164+
let platforms = rust
165+
.target
166+
.into_iter()
167+
.filter_map(|(target, content)| {
168+
if content.available {
169+
Some(target)
170+
} else {
171+
None
172+
}
173+
})
174+
.collect::<Vec<_>>();
175+
176+
blacksmith
177+
.previous_stable_versions
178+
.push((version.clone(), platforms));
179+
180+
previous_stable_version = SemVersion::from_str(&version);
181+
stable_version = previous_stable_version;
182+
}
183+
126184
blacksmith.last_update = unix_time();
127185
Ok(blacksmith)
128186
}
@@ -209,6 +267,45 @@ impl Blacksmith {
209267
buffer
210268
}
211269

270+
/// Generates tables of links to the previous stable standalone installer packages for
271+
/// each platform.
272+
fn generate_previous_stable_standalone_installers_tables(&self) -> String {
273+
let mut buffer = String::new();
274+
275+
for (stable_version, platforms) in &self.previous_stable_versions {
276+
writeln!(buffer, "## Stable ({})", stable_version).unwrap();
277+
writeln!(buffer, "").unwrap();
278+
279+
writeln!(buffer, "platform | stable ({})", stable_version).unwrap();
280+
writeln!(buffer, "---------|--------").unwrap();
281+
282+
for name in platforms {
283+
let extension = if name.contains("windows") {
284+
"msi"
285+
} else if name.contains("darwin") {
286+
"pkg"
287+
} else {
288+
"tar.gz"
289+
};
290+
291+
let stable_links =
292+
generate_standalone_links("rust", stable_version, name, extension);
293+
294+
writeln!(
295+
buffer,
296+
"`{name}` | {stable}",
297+
name = name,
298+
stable = stable_links,
299+
)
300+
.unwrap();
301+
}
302+
303+
writeln!(buffer, "").unwrap();
304+
}
305+
306+
buffer
307+
}
308+
212309
/// Generates a similar table to `generate_standalone_installers_table`
213310
/// except for the rust source code packages.
214311
fn generate_source_code_table(&self) -> String {
@@ -288,16 +385,23 @@ impl Preprocessor for Blacksmith {
288385

289386
let rustup_init_list = self.generate_rustup_init_list();
290387
let standalone_installers_table = self.generate_standalone_installers_table();
388+
let previous_stable_standalone_installers_tables =
389+
self.generate_previous_stable_standalone_installers_tables();
291390
let source_code_table = self.generate_source_code_table();
292391

293392
// TODO: Currently we're performing a global search for any of the
294393
// variables as that's the most flexible for adding more dynamic
295-
// content, and the time to traverse is fast enough to not be noticable.
394+
// content, and the time to traverse is fast enough to not be noticeable.
296395
// However if the processing time begins to become a bottleneck this
297396
// should change.
298397
for item in &mut book.sections {
299398
recursive_replace(item, "{{#rustup_init_list}}", &rustup_init_list);
300399
recursive_replace(item, "{{#installer_table}}", &standalone_installers_table);
400+
recursive_replace(
401+
item,
402+
"{{#previous_stable_standalone_installers_tables}}",
403+
&previous_stable_standalone_installers_tables,
404+
);
301405
recursive_replace(item, "{{#source_code_table}}", &source_code_table);
302406
}
303407

@@ -310,3 +414,96 @@ impl Preprocessor for Blacksmith {
310414
true
311415
}
312416
}
417+
418+
#[derive(Eq, PartialEq, Debug)]
419+
struct SemVersion {
420+
major: u32,
421+
minor: u32,
422+
// None represents that we haven't checked the highest patch version of this
423+
// stable major.minor Rust.
424+
patch: Option<u32>,
425+
}
426+
427+
impl SemVersion {
428+
fn new_with_patch(major: u32, minor: u32, patch: u32) -> Self {
429+
assert_eq!(major, 1); // We are not planning Rust v2 yet.
430+
Self {
431+
major,
432+
minor,
433+
patch: Some(patch),
434+
}
435+
}
436+
437+
fn from_str(version: &str) -> Self {
438+
let stable_version_parts = version
439+
.split('.')
440+
.map(|p| p.parse::<u32>().unwrap())
441+
.collect::<Vec<_>>();
442+
assert_eq!(stable_version_parts.len(), 3);
443+
444+
Self::new_with_patch(
445+
stable_version_parts[0],
446+
stable_version_parts[1],
447+
stable_version_parts[2],
448+
)
449+
}
450+
451+
fn previous(&self) -> Option<Self> {
452+
if self.patch.is_none() {
453+
panic!("Cannot get previous version without knowing the current patch number.");
454+
}
455+
456+
// TODO: Figure out how to get manifests for Rust <= 1.7
457+
if self.major == 1 && self.minor == 8 && self.patch.unwrap() == 0 {
458+
return None;
459+
}
460+
461+
if self.patch.is_some() && self.patch.unwrap() > 0 {
462+
return Some(Self {
463+
major: self.major,
464+
minor: self.minor,
465+
patch: Some(self.patch.unwrap() - 1),
466+
});
467+
}
468+
469+
if self.minor > 0 {
470+
return Some(Self {
471+
major: self.major,
472+
minor: self.minor - 1,
473+
patch: None,
474+
});
475+
}
476+
477+
// We don't plan Rust v2 yet.
478+
None
479+
}
480+
}
481+
482+
#[cfg(test)]
483+
mod tests {
484+
use super::*;
485+
486+
#[test]
487+
fn sem_version_previous_works_with_patch() {
488+
let latest = SemVersion::from_str("1.68.2");
489+
assert_eq!(latest, SemVersion::new_with_patch(1, 68, 2));
490+
let previous = latest.previous().unwrap();
491+
assert_eq!(previous, SemVersion::new_with_patch(1, 68, 1));
492+
let previous = previous.previous().unwrap();
493+
assert_eq!(previous, SemVersion::new_with_patch(1, 68, 0));
494+
let previous = previous.previous().unwrap();
495+
let expected = SemVersion {
496+
major: 1,
497+
minor: 67,
498+
patch: None,
499+
};
500+
assert_eq!(previous, expected);
501+
}
502+
503+
#[test]
504+
fn sem_version_previous_stops_after_1_8() {
505+
let latest = SemVersion::new_with_patch(1, 8, 0);
506+
let previous = latest.previous();
507+
assert_eq!(previous, None);
508+
}
509+
}

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
- [Moderation](./governance/moderation.md)
6868
- [Infrastructure](./infra/README.md)
6969
- [Other Installation Methods](./infra/other-installation-methods.md)
70+
- [Archive of Rust Stable Standalone Installers](./infra/archive-stable-version-installers.md)
7071
- [Release Channel Layout](./infra/channel-layout.md)
7172
- [Service Infrastructure](./infra/service-infrastructure.md)
7273
- [Team Maintenance](./infra/team-maintenance.md)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Archive of Rust Stable Standalone Installers
2+
3+
The official Rust standalone installers contain a single release of Rust, and
4+
are suitable for offline installation. They come in three forms: tarballs
5+
(extension `.tar.gz`), that work in any Unix-like environment, Windows
6+
installers (`.msi`), and Mac installers (`.pkg`). These installers come with
7+
`rustc`, `cargo`, `rustdoc`, the standard library, and the standard
8+
documentation, but do not provide access to additional cross-targets like
9+
`rustup` does.
10+
11+
The most common reasons to use these are:
12+
13+
- Offline installation
14+
- Preferring a more platform-integrated, graphical installer on Windows
15+
16+
Each of these binaries is signed with the [Rust signing key], which is
17+
[available on keybase.io], by the Rust build infrastructure, with [GPG]. In the
18+
tables below, the `.asc` files are the signatures.
19+
20+
{{#previous_stable_standalone_installers_tables}}

0 commit comments

Comments
 (0)