Skip to content

Commit 4446c65

Browse files
committed
Rollup merge of rust-lang#57011 - QuietMisdreavus:static-root-path, r=GuillaumeGomez
rustdoc: add new CLI flag to load static files from a different location This PR adds a new CLI flag to rustdoc, `--static-root-path`, which controls how rustdoc links pages to the CSS/JS/font static files bundled with the output. By default, these files are linked with a series of `../` which is calculated per-page to link it to the documentation root - i.e. a relative link to the directory given by `-o`. This is causing problems for docs.rs, because even though docs.rs has saved one copy of these files and is dispatching them dynamically, browsers have no way of knowing that these are the same files and can cache them. This can allow it to link these files as, for example, `/rustdoc.css` instead of `../../rustdoc.css`, creating a single location that the files are loaded from. I made sure to only change links for the *static* files, those that don't change between crates. Files like the search index, aliases, the source files listing, etc, are still linked with relative links. r? @GuillaumeGomez cc @onur
2 parents 5ba6a34 + 8dc8d7a commit 4446c65

File tree

6 files changed

+107
-22
lines changed

6 files changed

+107
-22
lines changed

src/doc/rustdoc/src/unstable-features.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,3 +402,18 @@ Using `index-page` option enables `enable-index-page` option as well.
402402
### `--enable-index-page`: generate a default index page for docs
403403

404404
This feature allows the generation of a default index-page which lists the generated crates.
405+
406+
### `--static-root-path`: control how static files are loaded in HTML output
407+
408+
Using this flag looks like this:
409+
410+
```bash
411+
$ rustdoc src/lib.rs -Z unstable-options --static-root-path '/cache/'
412+
```
413+
414+
This flag controls how rustdoc links to its static files on HTML pages. If you're hosting a lot of
415+
crates' docs generated by the same version of rustdoc, you can use this flag to cache rustdoc's CSS,
416+
JavaScript, and font files in a single location, rather than duplicating it once per "doc root"
417+
(grouping of crate docs generated into the same output directory, like with `cargo doc`). Per-crate
418+
files like the search index will still load from the documentation root, but anything that gets
419+
renamed with `--resource-suffix` will load from the given path.

src/librustdoc/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ pub struct RenderOptions {
181181
/// A file to use as the index page at the root of the output directory. Overrides
182182
/// `enable_index_page` to be true if set.
183183
pub index_page: Option<PathBuf>,
184+
/// An optional path to use as the location of static files. If not set, uses combinations of
185+
/// `../` to reach the documentation root.
186+
pub static_root_path: Option<String>,
184187

185188
// Options specific to reading standalone Markdown files
186189

@@ -433,6 +436,7 @@ impl Options {
433436
let markdown_playground_url = matches.opt_str("markdown-playground-url");
434437
let crate_version = matches.opt_str("crate-version");
435438
let enable_index_page = matches.opt_present("enable-index-page") || index_page.is_some();
439+
let static_root_path = matches.opt_str("static-root-path");
436440

437441
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
438442

@@ -471,6 +475,7 @@ impl Options {
471475
enable_minification,
472476
enable_index_page,
473477
index_page,
478+
static_root_path,
474479
markdown_no_toc,
475480
markdown_css,
476481
markdown_playground_url,

src/librustdoc/html/layout.rs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,20 @@ pub struct Page<'a> {
2626
pub title: &'a str,
2727
pub css_class: &'a str,
2828
pub root_path: &'a str,
29+
pub static_root_path: Option<&'a str>,
2930
pub description: &'a str,
3031
pub keywords: &'a str,
3132
pub resource_suffix: &'a str,
33+
pub extra_scripts: &'a [&'a str],
34+
pub static_extra_scripts: &'a [&'a str],
3235
}
3336

3437
pub fn render<T: fmt::Display, S: fmt::Display>(
3538
dst: &mut dyn io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T,
36-
css_file_extension: bool, themes: &[PathBuf], extra_scripts: &[&str])
39+
css_file_extension: bool, themes: &[PathBuf])
3740
-> io::Result<()>
3841
{
42+
let static_root_path = page.static_root_path.unwrap_or(page.root_path);
3943
write!(dst,
4044
"<!DOCTYPE html>\
4145
<html lang=\"en\">\
@@ -46,20 +50,20 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
4650
<meta name=\"description\" content=\"{description}\">\
4751
<meta name=\"keywords\" content=\"{keywords}\">\
4852
<title>{title}</title>\
49-
<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}normalize{suffix}.css\">\
50-
<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}rustdoc{suffix}.css\" \
53+
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}normalize{suffix}.css\">\
54+
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}rustdoc{suffix}.css\" \
5155
id=\"mainThemeStyle\">\
5256
{themes}\
53-
<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}dark{suffix}.css\">\
54-
<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}light{suffix}.css\" \
57+
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}dark{suffix}.css\">\
58+
<link rel=\"stylesheet\" type=\"text/css\" href=\"{static_root_path}light{suffix}.css\" \
5559
id=\"themeStyle\">\
56-
<script src=\"{root_path}storage{suffix}.js\"></script>\
57-
<noscript><link rel=\"stylesheet\" href=\"{root_path}noscript{suffix}.css\"></noscript>\
60+
<script src=\"{static_root_path}storage{suffix}.js\"></script>\
61+
<noscript><link rel=\"stylesheet\" href=\"{static_root_path}noscript{suffix}.css\"></noscript>\
5862
{css_extension}\
5963
{favicon}\
6064
{in_header}\
6165
<style type=\"text/css\">\
62-
#crate-search{{background-image:url(\"{root_path}down-arrow{suffix}.svg\");}}\
66+
#crate-search{{background-image:url(\"{static_root_path}down-arrow{suffix}.svg\");}}\
6367
</style>\
6468
</head>\
6569
<body class=\"rustdoc {css_class}\">\
@@ -77,11 +81,13 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
7781
</nav>\
7882
<div class=\"theme-picker\">\
7983
<button id=\"theme-picker\" aria-label=\"Pick another theme!\">\
80-
<img src=\"{root_path}brush{suffix}.svg\" width=\"18\" alt=\"Pick another theme!\">\
84+
<img src=\"{static_root_path}brush{suffix}.svg\" \
85+
width=\"18\" \
86+
alt=\"Pick another theme!\">\
8187
</button>\
8288
<div id=\"theme-choices\"></div>\
8389
</div>\
84-
<script src=\"{root_path}theme{suffix}.js\"></script>\
90+
<script src=\"{static_root_path}theme{suffix}.js\"></script>\
8591
<nav class=\"sub\">\
8692
<form class=\"search-form js-only\">\
8793
<div class=\"search-container\">\
@@ -96,7 +102,9 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
96102
type=\"search\">\
97103
</div>\
98104
<a id=\"settings-menu\" href=\"{root_path}settings.html\">\
99-
<img src=\"{root_path}wheel{suffix}.svg\" width=\"18\" alt=\"Change settings\">\
105+
<img src=\"{static_root_path}wheel{suffix}.svg\" \
106+
width=\"18\" \
107+
alt=\"Change settings\">\
100108
</a>\
101109
</div>\
102110
</form>\
@@ -157,19 +165,23 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
157165
window.currentCrate = \"{krate}\";\
158166
</script>\
159167
<script src=\"{root_path}aliases.js\"></script>\
160-
<script src=\"{root_path}main{suffix}.js\"></script>\
168+
<script src=\"{static_root_path}main{suffix}.js\"></script>\
169+
{static_extra_scripts}\
161170
{extra_scripts}\
162171
<script defer src=\"{root_path}search-index.js\"></script>\
163172
</body>\
164173
</html>",
165174
css_extension = if css_file_extension {
166-
format!("<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}theme{suffix}.css\">",
167-
root_path = page.root_path,
175+
format!("<link rel=\"stylesheet\" \
176+
type=\"text/css\" \
177+
href=\"{static_root_path}theme{suffix}.css\">",
178+
static_root_path = static_root_path,
168179
suffix=page.resource_suffix)
169180
} else {
170181
String::new()
171182
},
172183
content = *t,
184+
static_root_path = static_root_path,
173185
root_path = page.root_path,
174186
css_class = page.css_class,
175187
logo = if layout.logo.is_empty() {
@@ -197,12 +209,17 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
197209
.filter_map(|t| t.file_stem())
198210
.filter_map(|t| t.to_str())
199211
.map(|t| format!(r#"<link rel="stylesheet" type="text/css" href="{}{}{}.css">"#,
200-
page.root_path,
212+
static_root_path,
201213
t,
202214
page.resource_suffix))
203215
.collect::<String>(),
204216
suffix=page.resource_suffix,
205-
extra_scripts=extra_scripts.iter().map(|e| {
217+
static_extra_scripts=page.static_extra_scripts.iter().map(|e| {
218+
format!("<script src=\"{static_root_path}{extra_script}.js\"></script>",
219+
static_root_path=static_root_path,
220+
extra_script=e)
221+
}).collect::<String>(),
222+
extra_scripts=page.extra_scripts.iter().map(|e| {
206223
format!("<script src=\"{root_path}{extra_script}.js\"></script>",
207224
root_path=page.root_path,
208225
extra_script=e)

src/librustdoc/html/render.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ struct SharedContext {
140140
/// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
141141
/// "light-v2.css").
142142
pub resource_suffix: String,
143+
/// Optional path string to be used to load static files on output pages. If not set, uses
144+
/// combinations of `../` to reach the documentation root.
145+
pub static_root_path: Option<String>,
143146
}
144147

145148
impl SharedContext {
@@ -506,6 +509,7 @@ pub fn run(mut krate: clean::Crate,
506509
extension_css,
507510
extern_html_root_urls,
508511
resource_suffix,
512+
static_root_path,
509513
..
510514
} = options;
511515

@@ -533,6 +537,7 @@ pub fn run(mut krate: clean::Crate,
533537
sort_modules_alphabetically,
534538
themes,
535539
resource_suffix,
540+
static_root_path,
536541
};
537542

538543
// If user passed in `--playground-url` arg, we fill in crate name here
@@ -1080,9 +1085,12 @@ themePicker.onblur = handleThemeButtonsBlur;
10801085
title: "Index of crates",
10811086
css_class: "mod",
10821087
root_path: "./",
1088+
static_root_path: cx.shared.static_root_path.deref(),
10831089
description: "List of crates",
10841090
keywords: BASIC_KEYWORDS,
10851091
resource_suffix: &cx.shared.resource_suffix,
1092+
extra_scripts: &[],
1093+
static_extra_scripts: &[],
10861094
};
10871095
krates.push(krate.name.clone());
10881096
krates.sort();
@@ -1101,7 +1109,7 @@ themePicker.onblur = handleThemeButtonsBlur;
11011109
try_err!(layout::render(&mut w, &cx.shared.layout,
11021110
&page, &(""), &content,
11031111
cx.shared.css_file_extension.is_some(),
1104-
&cx.shared.themes, &[]), &dst);
1112+
&cx.shared.themes), &dst);
11051113
try_err!(w.flush(), &dst);
11061114
}
11071115
}
@@ -1366,15 +1374,17 @@ impl<'a> SourceCollector<'a> {
13661374
title: &title,
13671375
css_class: "source",
13681376
root_path: &root_path,
1377+
static_root_path: self.scx.static_root_path.deref(),
13691378
description: &desc,
13701379
keywords: BASIC_KEYWORDS,
13711380
resource_suffix: &self.scx.resource_suffix,
1381+
extra_scripts: &["source-files"],
1382+
static_extra_scripts: &[&format!("source-script{}", self.scx.resource_suffix)],
13721383
};
13731384
layout::render(&mut w, &self.scx.layout,
13741385
&page, &(""), &Source(contents),
13751386
self.scx.css_file_extension.is_some(),
1376-
&self.scx.themes, &["source-files",
1377-
&format!("source-script{}", page.resource_suffix)])?;
1387+
&self.scx.themes)?;
13781388
w.flush()?;
13791389
self.scx.local_sources.insert(p.clone(), href);
13801390
Ok(())
@@ -1956,9 +1966,12 @@ impl Context {
19561966
title: "List of all items in this crate",
19571967
css_class: "mod",
19581968
root_path: "../",
1969+
static_root_path: self.shared.static_root_path.deref(),
19591970
description: "List of all items in this crate",
19601971
keywords: BASIC_KEYWORDS,
19611972
resource_suffix: &self.shared.resource_suffix,
1973+
extra_scripts: &[],
1974+
static_extra_scripts: &[],
19621975
};
19631976
let sidebar = if let Some(ref version) = cache().crate_version {
19641977
format!("<p class='location'>Crate {}</p>\
@@ -1973,7 +1986,7 @@ impl Context {
19731986
try_err!(layout::render(&mut w, &self.shared.layout,
19741987
&page, &sidebar, &all,
19751988
self.shared.css_file_extension.is_some(),
1976-
&self.shared.themes, &[]),
1989+
&self.shared.themes),
19771990
&final_file);
19781991

19791992
// Generating settings page.
@@ -1993,7 +2006,7 @@ impl Context {
19932006
try_err!(layout::render(&mut w, &layout,
19942007
&page, &sidebar, &settings,
19952008
self.shared.css_file_extension.is_some(),
1996-
&themes, &[]),
2009+
&themes),
19972010
&settings_file);
19982011

19992012
Ok(())
@@ -2035,10 +2048,13 @@ impl Context {
20352048
let page = layout::Page {
20362049
css_class: tyname,
20372050
root_path: &self.root_path(),
2051+
static_root_path: self.shared.static_root_path.deref(),
20382052
title: &title,
20392053
description: &desc,
20402054
keywords: &keywords,
20412055
resource_suffix: &self.shared.resource_suffix,
2056+
extra_scripts: &[],
2057+
static_extra_scripts: &[],
20422058
};
20432059

20442060
{
@@ -2051,7 +2067,7 @@ impl Context {
20512067
&Sidebar{ cx: self, item: it },
20522068
&Item{ cx: self, item: it },
20532069
self.shared.css_file_extension.is_some(),
2054-
&self.shared.themes, &[])?;
2070+
&self.shared.themes)?;
20552071
} else {
20562072
let mut url = self.root_path();
20572073
if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {

src/librustdoc/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#![feature(crate_visibility_modifier)]
2626
#![feature(const_fn)]
2727
#![feature(drain_filter)]
28+
#![feature(inner_deref)]
2829

2930
#![recursion_limit="256"]
3031

@@ -338,6 +339,13 @@ fn opts() -> Vec<RustcOptGroup> {
338339
"enable-index-page",
339340
"To enable generation of the index page")
340341
}),
342+
unstable("static-root-path", |o| {
343+
o.optopt("",
344+
"static-root-path",
345+
"Path string to force loading static files from in output pages. \
346+
If not set, uses combinations of '../' to reach the documentation root.",
347+
"PATH")
348+
}),
341349
]
342350
}
343351

src/test/rustdoc/static-root-path.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags:-Z unstable-options --static-root-path /cache/
12+
13+
// @has static_root_path/struct.SomeStruct.html
14+
// @matches - '"/cache/main\.js"'
15+
// @!matches - '"\.\./main\.js"'
16+
// @matches - '"\.\./search-index\.js"'
17+
// @!matches - '"/cache/search-index\.js"'
18+
pub struct SomeStruct;
19+
20+
// @has src/static_root_path/static-root-path.rs.html
21+
// @matches - '"/cache/source-script\.js"'
22+
// @!matches - '"\.\./\.\./source-script\.js"'
23+
// @matches - '"\.\./\.\./source-files.js"'
24+
// @!matches - '"/cache/source-files\.js"'

0 commit comments

Comments
 (0)