Skip to content

Commit db113f5

Browse files
rustdoc: add --extern-html-root-url flag
1 parent 4122885 commit db113f5

File tree

3 files changed

+58
-3
lines changed

3 files changed

+58
-3
lines changed

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:

src/librustdoc/html/render.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,7 @@ pub fn derive_id(candidate: String) -> String {
507507

508508
/// Generates the documentation for `crate` into the directory `dst`
509509
pub fn run(mut krate: clean::Crate,
510+
extern_urls: BTreeMap<String, String>,
510511
external_html: &ExternalHtml,
511512
playground_url: Option<String>,
512513
dst: PathBuf,
@@ -636,8 +637,9 @@ pub fn run(mut krate: clean::Crate,
636637
},
637638
_ => PathBuf::new(),
638639
};
640+
let extern_url = extern_urls.get(&e.name).map(|u| &**u);
639641
cache.extern_locations.insert(n, (e.name.clone(), src_root,
640-
extern_location(e, &cx.dst)));
642+
extern_location(e, extern_url, &cx.dst)));
641643

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

10891091
/// Attempts to find where an external crate is located, given that we're
10901092
/// rendering in to the specified source destination.
1091-
fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation {
1093+
fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path)
1094+
-> ExternalLocation
1095+
{
10921096
// See if there's documentation generated into the local directory
10931097
let local_location = dst.join(&e.name);
10941098
if local_location.is_dir() {
10951099
return Local;
10961100
}
10971101

1102+
if let Some(url) = extern_url {
1103+
let mut url = url.to_string();
1104+
if !url.ends_with("/") {
1105+
url.push('/');
1106+
}
1107+
return Remote(url);
1108+
}
1109+
10981110
// Failing that, see if there's an attribute specifying where to find this
10991111
// external crate
11001112
e.attrs.lists("doc")

src/librustdoc/lib.rs

+29-1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,10 @@ pub fn opts() -> Vec<RustcOptGroup> {
158158
stable("extern", |o| {
159159
o.optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH")
160160
}),
161+
unstable("extern-html-root-url", |o| {
162+
o.optmulti("", "extern-html-root-url",
163+
"base URL to use for dependencies", "NAME=URL")
164+
}),
161165
stable("plugin-path", |o| {
162166
o.optmulti("", "plugin-path", "directory to load plugins from", "DIR")
163167
}),
@@ -423,6 +427,13 @@ pub fn main_args(args: &[String]) -> isize {
423427
return 1;
424428
}
425429
};
430+
let extern_urls = match parse_extern_html_roots(&matches) {
431+
Ok(ex) => ex,
432+
Err(err) => {
433+
diag.struct_err(err).emit();
434+
return 1;
435+
}
436+
};
426437

427438
let test_args = matches.opt_strs("test-args");
428439
let test_args: Vec<String> = test_args.iter()
@@ -521,7 +532,7 @@ pub fn main_args(args: &[String]) -> isize {
521532
info!("going to format");
522533
match output_format.as_ref().map(|s| &**s) {
523534
Some("html") | None => {
524-
html::render::run(krate, &external_html, playground_url,
535+
html::render::run(krate, extern_urls, &external_html, playground_url,
525536
output.unwrap_or(PathBuf::from("doc")),
526537
resource_suffix.unwrap_or(String::new()),
527538
passes.into_iter().collect(),
@@ -580,6 +591,23 @@ fn parse_externs(matches: &getopts::Matches) -> Result<Externs, String> {
580591
Ok(Externs::new(externs))
581592
}
582593

594+
/// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to
595+
/// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error
596+
/// describing the issue.
597+
fn parse_extern_html_roots(matches: &getopts::Matches)
598+
-> Result<BTreeMap<String, String>, &'static str>
599+
{
600+
let mut externs = BTreeMap::new();
601+
for arg in &matches.opt_strs("extern-html-root-url") {
602+
let mut parts = arg.splitn(2, '=');
603+
let name = parts.next().ok_or("--extern-html-root-url must not be empty")?;
604+
let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?;
605+
externs.insert(name.to_string(), url.to_string());
606+
}
607+
608+
Ok(externs)
609+
}
610+
583611
/// Interprets the input file as a rust source file, passing it through the
584612
/// compiler all the way through the analysis passes. The rustdoc output is then
585613
/// generated from the cleaned AST of the crate.

0 commit comments

Comments
 (0)