Skip to content

Commit aaa170b

Browse files
committed
Auto merge of #51384 - QuietMisdreavus:extern-version, r=GuillaumeGomez
rustdoc: add flag to control the html_root_url of dependencies The `--extern-html-root-url` flag in this PR allows one to override links to crates whose docs are not already available locally in the doc bundle. Docs.rs currently uses a version of this to make sure links to other crates go into that crate's docs.rs page. See the included test for intended use, but the idea is as follows: Calling rustdoc with `--extern-html-root-url crate=https://some-url.com` will cause rustdoc to override links that point to that crate to instead be replaced with a link rooted at `https://some-url.com/`. (e.g. for docs.rs this would be `https://docs.rs/crate/0.1.0` or the like.) Cheekily, rustup could use these options to redirect links to std/core/etc to instead point to locally-downloaded docs, if it so desired. Fixes #19603
2 parents 163adf2 + fcb54f6 commit aaa170b

File tree

4 files changed

+76
-3
lines changed

4 files changed

+76
-3
lines changed

Diff for: src/doc/rustdoc/src/unstable-features.md

+15
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,21 @@ This flag allows rustdoc to treat your rust code as the given edition. It will c
361361
the given edition as well. As with `rustc`, the default edition that `rustdoc` will use is `2015`
362362
(the first edition).
363363

364+
### `--extern-html-root-url`: control how rustdoc links to non-local crates
365+
366+
Using this flag looks like this:
367+
368+
```bash
369+
$ rustdoc src/lib.rs -Z unstable-options --extern-html-root-url some-crate=https://example.com/some-crate/1.0.1
370+
```
371+
372+
Ordinarily, when rustdoc wants to link to a type from a different crate, it looks in two places:
373+
docs that already exist in the output directory, or the `#![doc(doc_html_root)]` set in the other
374+
crate. However, if you want to link to docs that exist in neither of those places, you can use these
375+
flags to control that behavior. When the `--extern-html-root-url` flag is given with a name matching
376+
one of your dependencies, rustdoc use that URL for those docs. Keep in mind that if those docs exist
377+
in the output directory, those local docs will still override this flag.
378+
364379
### `-Z force-unstable-if-unmarked`
365380

366381
Using this flag looks like this:

Diff for: src/librustdoc/html/render.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ pub fn initial_ids() -> Vec<String> {
479479

480480
/// Generates the documentation for `crate` into the directory `dst`
481481
pub fn run(mut krate: clean::Crate,
482+
extern_urls: BTreeMap<String, String>,
482483
external_html: &ExternalHtml,
483484
playground_url: Option<String>,
484485
dst: PathBuf,
@@ -611,8 +612,9 @@ pub fn run(mut krate: clean::Crate,
611612
},
612613
_ => PathBuf::new(),
613614
};
615+
let extern_url = extern_urls.get(&e.name).map(|u| &**u);
614616
cache.extern_locations.insert(n, (e.name.clone(), src_root,
615-
extern_location(e, &cx.dst)));
617+
extern_location(e, extern_url, &cx.dst)));
616618

617619
let did = DefId { krate: n, index: CRATE_DEF_INDEX };
618620
cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
@@ -1096,13 +1098,23 @@ fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) wh
10961098

10971099
/// Attempts to find where an external crate is located, given that we're
10981100
/// rendering in to the specified source destination.
1099-
fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation {
1101+
fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path)
1102+
-> ExternalLocation
1103+
{
11001104
// See if there's documentation generated into the local directory
11011105
let local_location = dst.join(&e.name);
11021106
if local_location.is_dir() {
11031107
return Local;
11041108
}
11051109

1110+
if let Some(url) = extern_url {
1111+
let mut url = url.to_string();
1112+
if !url.ends_with("/") {
1113+
url.push('/');
1114+
}
1115+
return Remote(url);
1116+
}
1117+
11061118
// Failing that, see if there's an attribute specifying where to find this
11071119
// external crate
11081120
e.attrs.lists("doc")

Diff for: src/librustdoc/lib.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ fn opts() -> Vec<RustcOptGroup> {
162162
stable("extern", |o| {
163163
o.optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH")
164164
}),
165+
unstable("extern-html-root-url", |o| {
166+
o.optmulti("", "extern-html-root-url",
167+
"base URL to use for dependencies", "NAME=URL")
168+
}),
165169
stable("plugin-path", |o| {
166170
o.optmulti("", "plugin-path", "removed", "DIR")
167171
}),
@@ -453,6 +457,13 @@ fn main_args(args: &[String]) -> isize {
453457
return 1;
454458
}
455459
};
460+
let extern_urls = match parse_extern_html_roots(&matches) {
461+
Ok(ex) => ex,
462+
Err(err) => {
463+
diag.struct_err(err).emit();
464+
return 1;
465+
}
466+
};
456467

457468
let test_args = matches.opt_strs("test-args");
458469
let test_args: Vec<String> = test_args.iter()
@@ -553,7 +564,7 @@ fn main_args(args: &[String]) -> isize {
553564
info!("going to format");
554565
match output_format.as_ref().map(|s| &**s) {
555566
Some("html") | None => {
556-
html::render::run(krate, &external_html, playground_url,
567+
html::render::run(krate, extern_urls, &external_html, playground_url,
557568
output.unwrap_or(PathBuf::from("doc")),
558569
resource_suffix.unwrap_or(String::new()),
559570
passes.into_iter().collect(),
@@ -612,6 +623,23 @@ fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
612623
Ok(Externs::new(externs))
613624
}
614625

626+
/// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to
627+
/// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error
628+
/// describing the issue.
629+
fn parse_extern_html_roots(matches: &getopts::Matches)
630+
-> Result<BTreeMap<String, String>, &'static str>
631+
{
632+
let mut externs = BTreeMap::new();
633+
for arg in &matches.opt_strs("extern-html-root-url") {
634+
let mut parts = arg.splitn(2, '=');
635+
let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
636+
let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
637+
externs.insert(name.to_string(), url.to_string());
638+
}
639+
640+
Ok(externs)
641+
}
642+
615643
/// Interprets the input file as a rust source file, passing it through the
616644
/// compiler all the way through the analysis passes. The rustdoc output is then
617645
/// generated from the cleaned AST of the crate.

Diff for: src/test/rustdoc/extern-html-root-url.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2012-2014 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+
// ignore-tidy-linelength
12+
13+
// compile-flags:-Z unstable-options --extern-html-root-url core=https://example.com/core/0.1.0
14+
15+
// @has extern_html_root_url/index.html
16+
// @has - '//a/@href' 'https://example.com/core/0.1.0/core/iter/index.html'
17+
#[doc(no_inline)]
18+
pub use std::iter;

0 commit comments

Comments
 (0)