@@ -15,6 +15,7 @@ use mdbook::{
1515
1616const CHANNELS : & [ & str ] = & [ "stable" , "beta" , "nightly" ] ;
1717const CHANNEL_URL_PREFIX : & str = "https://static.rust-lang.org/dist/channel-rust-" ;
18+ const MANIFESTS_URL : & str = "https://static.rust-lang.org/manifests.txt" ;
1819const RUSTUP_URLS : & str =
1920 "https://raw.githubusercontent.com/rust-lang/rustup.rs/stable/ci/cloudfront-invalidation.txt" ;
2021
@@ -45,6 +46,8 @@ pub struct Blacksmith {
4546 rustup : Vec < String > ,
4647 stable_version : Option < String > ,
4748 platforms : BTreeMap < String , Platform > ,
49+ #[ serde( default ) ]
50+ previous_stable_versions : Vec < ( String , Vec < String > ) > ,
4851}
4952
5053impl Blacksmith {
@@ -123,6 +126,140 @@ impl Blacksmith {
123126 }
124127 }
125128
129+ let latest_stable_version = & blacksmith. stable_version . clone ( ) . unwrap ( ) ;
130+
131+ // We need to use a map to deduplicate entries and sort them in the correct order.
132+ // There are multiple entries for stable 1.8.0, 1.14.0, 1.15.1, 1.49.0 in MANIFESTS_URL.
133+ // Keys contain (minor_version, patch_version) and values contain (full_version, platforms).
134+ let mut previous_stable_version_map: BTreeMap < ( u32 , u32 ) , ( String , Vec < String > ) > =
135+ BTreeMap :: new ( ) ;
136+
137+ // Go over stable versions in https://static.rust-lang.org/manifests.txt in reverse order.
138+ let manifests_content = reqwest:: blocking:: get ( MANIFESTS_URL ) ?. text ( ) ?;
139+ let stable_manifest_url_regex = regex:: Regex :: new (
140+ r"^static\.rust-lang\.org/dist/\d{4}-\d{2}-\d{2}/channel-rust-1\.(\d+)\.(\d+)\.toml$" ,
141+ )
142+ . unwrap ( ) ;
143+ for manifest_url in manifests_content. lines ( ) . rev ( ) {
144+ let minor;
145+ let patch;
146+
147+ // Check if it's a stable version.
148+ if let Some ( captures) = stable_manifest_url_regex. captures ( & ( manifest_url) ) {
149+ minor = captures. get ( 1 ) . unwrap ( ) . as_str ( ) . parse :: < u32 > ( ) . unwrap ( ) ;
150+ patch = captures. get ( 2 ) . unwrap ( ) . as_str ( ) . parse :: < u32 > ( ) . unwrap ( ) ;
151+ } else {
152+ continue ;
153+ }
154+
155+ let full_version = format ! ( "1.{}.{}" , minor, patch) ;
156+
157+ // Check if we already processed that version.
158+ if previous_stable_version_map. contains_key ( & ( minor, patch) ) {
159+ continue ;
160+ }
161+
162+ // Skip latest stable version.
163+ if & full_version == latest_stable_version {
164+ continue ;
165+ }
166+
167+ // Download https://static.rust-lang.org/dist/channel-rust-{major.minor.patch}.toml and process it.
168+ let channel_url = format ! ( "{}{}.toml" , CHANNEL_URL_PREFIX , full_version) ;
169+
170+ let content = reqwest:: blocking:: get ( & channel_url) ?. text ( ) ?;
171+ let rust = toml:: from_str :: < crate :: channel:: Channel > ( & content) ?
172+ . pkg
173+ . rust ;
174+
175+ log:: info!(
176+ "Found {} targets for stable v{}" ,
177+ rust. target. len( ) ,
178+ rust. version
179+ ) ;
180+
181+ let version = rust. version . split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ;
182+
183+ // Sanity check.
184+ assert_eq ! ( & full_version, & version) ;
185+
186+ let platforms = rust
187+ . target
188+ . into_iter ( )
189+ . filter_map ( |( target, content) | {
190+ if content. available {
191+ Some ( target)
192+ } else {
193+ None
194+ }
195+ } )
196+ . collect :: < Vec < _ > > ( ) ;
197+
198+ previous_stable_version_map. insert ( ( minor, patch) , ( version, platforms) ) ;
199+ }
200+
201+ // There are no manifests for stable versions before 1.8.0,
202+ // so we hardcode them instead.
203+ // i686-pc-windows-msvc wasn't supported until version 1.3.0.
204+ for minor in 3 ..8 {
205+ previous_stable_version_map. insert (
206+ ( minor, 0 ) ,
207+ (
208+ format ! ( "1.{}.0" , minor) ,
209+ Vec :: from ( [
210+ "i686-apple-darwin" . to_string ( ) ,
211+ "i686-pc-windows-gnu" . to_string ( ) ,
212+ "i686-pc-windows-msvc" . to_string ( ) ,
213+ "i686-unknown-linux-gnu" . to_string ( ) ,
214+ "x86_64-apple-darwin" . to_string ( ) ,
215+ "x86_64-pc-windows-gnu" . to_string ( ) ,
216+ "x86_64-pc-windows-msvc" . to_string ( ) ,
217+ "x86_64-unknown-linux-gnu" . to_string ( ) ,
218+ ] ) ,
219+ ) ,
220+ ) ;
221+ }
222+
223+ // x86_64-pc-windows-msvc wasn't supported until version 1.2.0.
224+ previous_stable_version_map. insert (
225+ ( 2 , 0 ) ,
226+ (
227+ "1.2.0" . to_string ( ) ,
228+ Vec :: from ( [
229+ "i686-apple-darwin" . to_string ( ) ,
230+ "i686-pc-windows-gnu" . to_string ( ) ,
231+ "i686-unknown-linux-gnu" . to_string ( ) ,
232+ "x86_64-apple-darwin" . to_string ( ) ,
233+ "x86_64-pc-windows-gnu" . to_string ( ) ,
234+ "x86_64-pc-windows-msvc" . to_string ( ) ,
235+ "x86_64-unknown-linux-gnu" . to_string ( ) ,
236+ ] ) ,
237+ ) ,
238+ ) ;
239+
240+ for minor in 0 ..2 {
241+ previous_stable_version_map. insert (
242+ ( minor, 0 ) ,
243+ (
244+ format ! ( "1.{}.0" , minor) ,
245+ Vec :: from ( [
246+ "i686-apple-darwin" . to_string ( ) ,
247+ "i686-pc-windows-gnu" . to_string ( ) ,
248+ "i686-unknown-linux-gnu" . to_string ( ) ,
249+ "x86_64-apple-darwin" . to_string ( ) ,
250+ "x86_64-pc-windows-gnu" . to_string ( ) ,
251+ "x86_64-unknown-linux-gnu" . to_string ( ) ,
252+ ] ) ,
253+ ) ,
254+ ) ;
255+ }
256+
257+ for ( _, ( version, platforms) ) in previous_stable_version_map. into_iter ( ) . rev ( ) {
258+ blacksmith
259+ . previous_stable_versions
260+ . push ( ( version, platforms) ) ;
261+ }
262+
126263 blacksmith. last_update = unix_time ( ) ;
127264 Ok ( blacksmith)
128265 }
@@ -169,28 +306,28 @@ impl Blacksmith {
169306 writeln ! ( buffer, "---------|--------|------|--------" ) . unwrap ( ) ;
170307
171308 for ( name, platform) in & self . platforms {
172- let extension = if name. contains ( "windows" ) {
173- "msi"
309+ let extensions : & [ & str ] = if name. contains ( "windows" ) {
310+ & [ "msi" , "tar.xz" ]
174311 } else if name. contains ( "darwin" ) {
175- "pkg"
312+ & [ "pkg" , "tar.xz" ]
176313 } else {
177- "tar.xz"
314+ & [ "tar.xz" ]
178315 } ;
179316
180317 let stable_links = platform
181318 . stable
182319 . as_ref ( )
183- . map ( |version| generate_standalone_links ( "rust" , version, name, extension ) )
320+ . map ( |version| generate_standalone_links ( "rust" , version, name, extensions ) )
184321 . unwrap_or_else ( String :: new) ;
185322
186323 let beta_links = if platform. beta {
187- generate_standalone_links ( "rust" , "beta" , name, extension )
324+ generate_standalone_links ( "rust" , "beta" , name, extensions )
188325 } else {
189326 String :: new ( )
190327 } ;
191328
192329 let nightly_links = if platform. nightly {
193- generate_standalone_links ( "rust" , "nightly" , name, extension )
330+ generate_standalone_links ( "rust" , "nightly" , name, extensions )
194331 } else {
195332 String :: new ( )
196333 } ;
@@ -209,6 +346,45 @@ impl Blacksmith {
209346 buffer
210347 }
211348
349+ /// Generates tables of links to the previous stable standalone installer packages for
350+ /// each platform.
351+ fn generate_previous_stable_standalone_installers_tables ( & self ) -> String {
352+ let mut buffer = String :: new ( ) ;
353+
354+ for ( stable_version, platforms) in & self . previous_stable_versions {
355+ writeln ! ( buffer, "## Stable ({})" , stable_version) . unwrap ( ) ;
356+ writeln ! ( buffer, "" ) . unwrap ( ) ;
357+
358+ writeln ! ( buffer, "platform | stable ({})" , stable_version) . unwrap ( ) ;
359+ writeln ! ( buffer, "---------|--------" ) . unwrap ( ) ;
360+
361+ for name in platforms {
362+ let extensions: & [ & str ] = if name. contains ( "windows" ) {
363+ & [ "msi" , "tar.gz" ]
364+ } else if name. contains ( "darwin" ) {
365+ & [ "pkg" , "tar.gz" ]
366+ } else {
367+ & [ "tar.gz" ]
368+ } ;
369+
370+ let stable_links =
371+ generate_standalone_links ( "rust" , stable_version, name, extensions) ;
372+
373+ writeln ! (
374+ buffer,
375+ "`{name}` | {stable}" ,
376+ name = name,
377+ stable = stable_links,
378+ )
379+ . unwrap ( ) ;
380+ }
381+
382+ writeln ! ( buffer, "" ) . unwrap ( ) ;
383+ }
384+
385+ buffer
386+ }
387+
212388 /// Generates a similar table to `generate_standalone_installers_table`
213389 /// except for the rust source code packages.
214390 fn generate_source_code_table ( & self ) -> String {
@@ -224,15 +400,15 @@ impl Blacksmith {
224400 buffer,
225401 "stable ({}) | {}" ,
226402 stable_version,
227- generate_standalone_links( "rustc" , stable_version, "src" , "tar.xz" )
403+ generate_standalone_links( "rustc" , stable_version, "src" , & [ "tar.xz" ] )
228404 )
229405 . unwrap ( ) ;
230406 } else {
231407 writeln ! (
232408 buffer,
233409 "{} | {}" ,
234410 channel,
235- generate_standalone_links( "rustc" , & channel, "src" , "tar.xz" )
411+ generate_standalone_links( "rustc" , & channel, "src" , & [ "tar.xz" ] )
236412 )
237413 . unwrap ( ) ;
238414 }
@@ -244,20 +420,27 @@ impl Blacksmith {
244420
245421/// Generates links to standalone installers provided a rust version or channel,
246422/// target name, and file extension.
247- fn generate_standalone_links ( base : & str , stem : & str , name : & str , extension : & str ) -> String {
248- let url = format ! (
249- "https://static.rust-lang.org/dist/{base}-{stem}-{name}.{extension}" ,
250- extension = extension,
251- name = name,
252- stem = stem,
253- base = base,
254- ) ;
255-
256- format ! (
257- "[{extension}]({url}) <br> [{extension}.asc]({url}.asc)" ,
258- extension = extension,
259- url = url,
260- )
423+ fn generate_standalone_links ( base : & str , stem : & str , name : & str , extensions : & [ & str ] ) -> String {
424+ let mut result = String :: new ( ) ;
425+ for extension in extensions {
426+ if !result. is_empty ( ) {
427+ result. push_str ( " <br> " ) ;
428+ }
429+ let url = format ! (
430+ "https://static.rust-lang.org/dist/{base}-{stem}-{name}.{extension}" ,
431+ extension = extension,
432+ name = name,
433+ stem = stem,
434+ base = base,
435+ ) ;
436+
437+ result. push_str ( & format ! (
438+ "[{extension}]({url}) <br> [{extension}.asc]({url}.asc)" ,
439+ extension = extension,
440+ url = url,
441+ ) ) ;
442+ }
443+ result
261444}
262445
263446fn unix_time ( ) -> Option < u64 > {
@@ -288,16 +471,23 @@ impl Preprocessor for Blacksmith {
288471
289472 let rustup_init_list = self . generate_rustup_init_list ( ) ;
290473 let standalone_installers_table = self . generate_standalone_installers_table ( ) ;
474+ let previous_stable_standalone_installers_tables =
475+ self . generate_previous_stable_standalone_installers_tables ( ) ;
291476 let source_code_table = self . generate_source_code_table ( ) ;
292477
293478 // TODO: Currently we're performing a global search for any of the
294479 // 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 .
480+ // content, and the time to traverse is fast enough to not be noticeable .
296481 // However if the processing time begins to become a bottleneck this
297482 // should change.
298483 for item in & mut book. sections {
299484 recursive_replace ( item, "{{#rustup_init_list}}" , & rustup_init_list) ;
300485 recursive_replace ( item, "{{#installer_table}}" , & standalone_installers_table) ;
486+ recursive_replace (
487+ item,
488+ "{{#previous_stable_standalone_installers_tables}}" ,
489+ & previous_stable_standalone_installers_tables,
490+ ) ;
301491 recursive_replace ( item, "{{#source_code_table}}" , & source_code_table) ;
302492 }
303493
0 commit comments