diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index a91c2cd71cde3..a23dbd308244f 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -361,6 +361,21 @@ This flag allows rustdoc to treat your rust code as the given edition. It will c the given edition as well. As with `rustc`, the default edition that `rustdoc` will use is `2015` (the first edition). +### `--extern-html-root-url`: control how rustdoc links to non-local crates + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs -Z unstable-options --extern-html-root-url some-crate=https://example.com/some-crate/1.0.1 +``` + +Ordinarily, when rustdoc wants to link to a type from a different crate, it looks in two places: +docs that already exist in the output directory, or the `#![doc(doc_html_root)]` set in the other +crate. However, if you want to link to docs that exist in neither of those places, you can use these +flags to control that behavior. When the `--extern-html-root-url` flag is given with a name matching +one of your dependencies, rustdoc use that URL for those docs. Keep in mind that if those docs exist +in the output directory, those local docs will still override this flag. + ### `-Z force-unstable-if-unmarked` Using this flag looks like this: diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 1abe01dd0ac20..be51ab5484da7 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -479,6 +479,7 @@ pub fn initial_ids() -> Vec { /// Generates the documentation for `crate` into the directory `dst` pub fn run(mut krate: clean::Crate, + extern_urls: BTreeMap, external_html: &ExternalHtml, playground_url: Option, dst: PathBuf, @@ -611,8 +612,9 @@ pub fn run(mut krate: clean::Crate, }, _ => PathBuf::new(), }; + let extern_url = extern_urls.get(&e.name).map(|u| &**u); cache.extern_locations.insert(n, (e.name.clone(), src_root, - extern_location(e, &cx.dst))); + extern_location(e, extern_url, &cx.dst))); let did = DefId { krate: n, index: CRATE_DEF_INDEX }; cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); @@ -1096,13 +1098,23 @@ fn clean_srcpath(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) wh /// Attempts to find where an external crate is located, given that we're /// rendering in to the specified source destination. -fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation { +fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path) + -> ExternalLocation +{ // See if there's documentation generated into the local directory let local_location = dst.join(&e.name); if local_location.is_dir() { return Local; } + if let Some(url) = extern_url { + let mut url = url.to_string(); + if !url.ends_with("/") { + url.push('/'); + } + return Remote(url); + } + // Failing that, see if there's an attribute specifying where to find this // external crate e.attrs.lists("doc") diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 279151eb26def..df885d8a772fb 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -162,6 +162,10 @@ fn opts() -> Vec { stable("extern", |o| { o.optmulti("", "extern", "pass an --extern to rustc", "NAME=PATH") }), + unstable("extern-html-root-url", |o| { + o.optmulti("", "extern-html-root-url", + "base URL to use for dependencies", "NAME=URL") + }), stable("plugin-path", |o| { o.optmulti("", "plugin-path", "removed", "DIR") }), @@ -453,6 +457,13 @@ fn main_args(args: &[String]) -> isize { return 1; } }; + let extern_urls = match parse_extern_html_roots(&matches) { + Ok(ex) => ex, + Err(err) => { + diag.struct_err(err).emit(); + return 1; + } + }; let test_args = matches.opt_strs("test-args"); let test_args: Vec = test_args.iter() @@ -553,7 +564,7 @@ fn main_args(args: &[String]) -> isize { info!("going to format"); match output_format.as_ref().map(|s| &**s) { Some("html") | None => { - html::render::run(krate, &external_html, playground_url, + html::render::run(krate, extern_urls, &external_html, playground_url, output.unwrap_or(PathBuf::from("doc")), resource_suffix.unwrap_or(String::new()), passes.into_iter().collect(), @@ -612,6 +623,23 @@ fn parse_externs(matches: &getopts::Matches) -> Result { Ok(Externs::new(externs)) } +/// Extracts `--extern-html-root-url` arguments from `matches` and returns a map of crate names to +/// the given URLs. If an `--extern-html-root-url` argument was ill-formed, returns an error +/// describing the issue. +fn parse_extern_html_roots(matches: &getopts::Matches) + -> Result, &'static str> +{ + let mut externs = BTreeMap::new(); + for arg in &matches.opt_strs("extern-html-root-url") { + let mut parts = arg.splitn(2, '='); + let name = parts.next().ok_or("--extern-html-root-url must not be empty")?; + let url = parts.next().ok_or("--extern-html-root-url must be of the form name=url")?; + externs.insert(name.to_string(), url.to_string()); + } + + Ok(externs) +} + /// Interprets the input file as a rust source file, passing it through the /// compiler all the way through the analysis passes. The rustdoc output is then /// generated from the cleaned AST of the crate. diff --git a/src/test/rustdoc/extern-html-root-url.rs b/src/test/rustdoc/extern-html-root-url.rs new file mode 100644 index 0000000000000..c8a13bec9d321 --- /dev/null +++ b/src/test/rustdoc/extern-html-root-url.rs @@ -0,0 +1,18 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-tidy-linelength + +// compile-flags:-Z unstable-options --extern-html-root-url core=https://example.com/core/0.1.0 + +// @has extern_html_root_url/index.html +// @has - '//a/@href' 'https://example.com/core/0.1.0/core/iter/index.html' +#[doc(no_inline)] +pub use std::iter;