mirror of https://github.com/rust-lang/cargo
Add support for rustdoc root URL mappings.
This commit is contained in:
parent
ff9126d0d2
commit
e0f9643b0f
|
@ -73,6 +73,7 @@
|
|||
//! mtime of sources | ✓[^3] |
|
||||
//! RUSTFLAGS/RUSTDOCFLAGS | ✓ |
|
||||
//! LTO flags | ✓ |
|
||||
//! config settings[^5] | ✓ |
|
||||
//! is_std | | ✓
|
||||
//!
|
||||
//! [^1]: Build script and bin dependencies are not included.
|
||||
|
@ -82,6 +83,9 @@
|
|||
//! [^4]: `__CARGO_DEFAULT_LIB_METADATA` is set by rustbuild to embed the
|
||||
//! release channel (bootstrap/stable/beta/nightly) in libstd.
|
||||
//!
|
||||
//! [^5]: Config settings that are not otherwise captured anywhere else.
|
||||
//! Currently, this is only `doc.extern-map`.
|
||||
//!
|
||||
//! When deciding what should go in the Metadata vs the Fingerprint, consider
|
||||
//! that some files (like dylibs) do not have a hash in their filename. Thus,
|
||||
//! if a value changes, only the fingerprint will detect the change (consider,
|
||||
|
@ -533,6 +537,8 @@ pub struct Fingerprint {
|
|||
/// "description", which are exposed as environment variables during
|
||||
/// compilation.
|
||||
metadata: u64,
|
||||
/// Hash of various config settings that change how things are compiled.
|
||||
config: u64,
|
||||
/// Description of whether the filesystem status for this unit is up to date
|
||||
/// or should be considered stale.
|
||||
#[serde(skip)]
|
||||
|
@ -746,6 +752,7 @@ impl Fingerprint {
|
|||
memoized_hash: Mutex::new(None),
|
||||
rustflags: Vec::new(),
|
||||
metadata: 0,
|
||||
config: 0,
|
||||
fs_status: FsStatus::Stale,
|
||||
outputs: Vec::new(),
|
||||
}
|
||||
|
@ -806,6 +813,9 @@ impl Fingerprint {
|
|||
if self.metadata != old.metadata {
|
||||
bail!("metadata changed")
|
||||
}
|
||||
if self.config != old.config {
|
||||
bail!("configuration settings have changed")
|
||||
}
|
||||
let my_local = self.local.lock().unwrap();
|
||||
let old_local = old.local.lock().unwrap();
|
||||
if my_local.len() != old_local.len() {
|
||||
|
@ -1040,12 +1050,13 @@ impl hash::Hash for Fingerprint {
|
|||
ref deps,
|
||||
ref local,
|
||||
metadata,
|
||||
config,
|
||||
ref rustflags,
|
||||
..
|
||||
} = *self;
|
||||
let local = local.lock().unwrap();
|
||||
(
|
||||
rustc, features, target, path, profile, &*local, metadata, rustflags,
|
||||
rustc, features, target, path, profile, &*local, metadata, config, rustflags,
|
||||
)
|
||||
.hash(h);
|
||||
|
||||
|
@ -1252,6 +1263,14 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
|
|||
// Include metadata since it is exposed as environment variables.
|
||||
let m = unit.pkg.manifest().metadata();
|
||||
let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository));
|
||||
let config = if unit.mode.is_doc() && cx.bcx.config.cli_unstable().rustdoc_map {
|
||||
cx.bcx
|
||||
.config
|
||||
.doc_extern_map()
|
||||
.map_or(0, |map| util::hash_u64(map))
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Ok(Fingerprint {
|
||||
rustc: util::hash_u64(&cx.bcx.rustc().verbose_version),
|
||||
target: util::hash_u64(&unit.target),
|
||||
|
@ -1264,6 +1283,7 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
|
|||
local: Mutex::new(local),
|
||||
memoized_hash: Mutex::new(None),
|
||||
metadata,
|
||||
config,
|
||||
rustflags: extra_flags,
|
||||
fs_status: FsStatus::Stale,
|
||||
outputs,
|
||||
|
|
|
@ -13,6 +13,7 @@ mod layout;
|
|||
mod links;
|
||||
mod lto;
|
||||
mod output_depinfo;
|
||||
pub mod rustdoc;
|
||||
pub mod standard_lib;
|
||||
mod timings;
|
||||
mod unit;
|
||||
|
@ -570,6 +571,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
|
|||
}
|
||||
|
||||
build_deps_args(&mut rustdoc, cx, unit)?;
|
||||
rustdoc::add_root_urls(cx, unit, &mut rustdoc)?;
|
||||
|
||||
rustdoc.args(bcx.rustdocflags_args(unit));
|
||||
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
//! Utilities for building with rustdoc.
|
||||
|
||||
use crate::core::compiler::context::Context;
|
||||
use crate::core::compiler::unit::Unit;
|
||||
use crate::core::compiler::CompileKind;
|
||||
use crate::sources::CRATES_IO_REGISTRY;
|
||||
use crate::util::errors::{internal, CargoResult};
|
||||
use crate::util::ProcessBuilder;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::hash;
|
||||
use url::Url;
|
||||
|
||||
/// Mode used for `std`.
|
||||
#[derive(Debug, Hash)]
|
||||
pub enum RustdocExternMode {
|
||||
/// Use a local `file://` URL.
|
||||
Local,
|
||||
/// Use a remote URL to https://doc.rust-lang.org/ (default).
|
||||
Remote,
|
||||
/// An arbitrary URL.
|
||||
Url(String),
|
||||
}
|
||||
|
||||
impl From<String> for RustdocExternMode {
|
||||
fn from(s: String) -> RustdocExternMode {
|
||||
match s.as_ref() {
|
||||
"local" => RustdocExternMode::Local,
|
||||
"remote" => RustdocExternMode::Remote,
|
||||
_ => RustdocExternMode::Url(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RustdocExternMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RustdocExternMode::Local => "local".fmt(f),
|
||||
RustdocExternMode::Remote => "remote".fmt(f),
|
||||
RustdocExternMode::Url(s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Deserialize<'de> for RustdocExternMode {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Ok(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct RustdocExternMap {
|
||||
registries: HashMap<String, String>,
|
||||
std: Option<RustdocExternMode>,
|
||||
}
|
||||
|
||||
impl hash::Hash for RustdocExternMap {
|
||||
fn hash<H: hash::Hasher>(&self, into: &mut H) {
|
||||
self.std.hash(into);
|
||||
for (key, value) in &self.registries {
|
||||
key.hash(into);
|
||||
value.hash(into);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_root_urls(
|
||||
cx: &Context<'_, '_>,
|
||||
unit: &Unit,
|
||||
rustdoc: &mut ProcessBuilder,
|
||||
) -> CargoResult<()> {
|
||||
let config = cx.bcx.config;
|
||||
if !config.cli_unstable().rustdoc_map {
|
||||
log::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag");
|
||||
return Ok(());
|
||||
}
|
||||
let map = config.doc_extern_map()?;
|
||||
if map.registries.len() == 0 && map.std.is_none() {
|
||||
// Skip doing unnecessary work.
|
||||
return Ok(());
|
||||
}
|
||||
let mut unstable_opts = false;
|
||||
// Collect mapping of registry name -> index url.
|
||||
let name2url: HashMap<&String, Url> = map
|
||||
.registries
|
||||
.keys()
|
||||
.filter_map(|name| {
|
||||
if let Ok(index_url) = config.get_registry_index(name) {
|
||||
return Some((name, index_url));
|
||||
} else {
|
||||
log::warn!(
|
||||
"`doc.extern-map.{}` specifies a registry that is not defined",
|
||||
name
|
||||
);
|
||||
return None;
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
for dep in cx.unit_deps(unit) {
|
||||
if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
|
||||
for (registry, location) in &map.registries {
|
||||
let sid = dep.unit.pkg.package_id().source_id();
|
||||
let matches_registry = || -> bool {
|
||||
if !sid.is_registry() {
|
||||
return false;
|
||||
}
|
||||
if sid.is_default_registry() {
|
||||
return registry == CRATES_IO_REGISTRY;
|
||||
}
|
||||
if let Some(index_url) = name2url.get(registry) {
|
||||
return index_url == sid.url();
|
||||
}
|
||||
false
|
||||
};
|
||||
if matches_registry() {
|
||||
let mut url = location.clone();
|
||||
if !url.contains("{pkg_name}") && !url.contains("{version}") {
|
||||
if !url.ends_with('/') {
|
||||
url.push('/');
|
||||
}
|
||||
url.push_str("{pkg_name}/{version}/");
|
||||
}
|
||||
let url = url
|
||||
.replace("{pkg_name}", &dep.unit.pkg.name())
|
||||
.replace("{version}", &dep.unit.pkg.version().to_string());
|
||||
rustdoc.arg("--extern-html-root-url");
|
||||
rustdoc.arg(format!("{}={}", dep.unit.target.crate_name(), url));
|
||||
unstable_opts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let std_url = match &map.std {
|
||||
None | Some(RustdocExternMode::Remote) => None,
|
||||
Some(RustdocExternMode::Local) => {
|
||||
let sysroot = &cx.bcx.target_data.info(CompileKind::Host).sysroot;
|
||||
let html_root = sysroot.join("share").join("doc").join("rust").join("html");
|
||||
if html_root.exists() {
|
||||
let url = Url::from_file_path(&html_root).map_err(|()| {
|
||||
internal(format!(
|
||||
"`{}` failed to convert to URL",
|
||||
html_root.display()
|
||||
))
|
||||
})?;
|
||||
Some(url.to_string())
|
||||
} else {
|
||||
log::warn!(
|
||||
"`doc.extern-map.std` is \"local\", but local docs don't appear to exist at {}",
|
||||
html_root.display()
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
Some(RustdocExternMode::Url(s)) => Some(s.to_string()),
|
||||
};
|
||||
if let Some(url) = std_url {
|
||||
for name in &["std", "core", "alloc", "proc_macro"] {
|
||||
rustdoc.arg("--extern-html-root-url");
|
||||
rustdoc.arg(format!("{}={}", name, url));
|
||||
unstable_opts = true;
|
||||
}
|
||||
}
|
||||
|
||||
if unstable_opts {
|
||||
rustdoc.arg("-Zunstable-options");
|
||||
}
|
||||
Ok(())
|
||||
}
|
|
@ -356,6 +356,7 @@ pub struct CliUnstable {
|
|||
pub crate_versions: bool,
|
||||
pub separate_nightlies: bool,
|
||||
pub multitarget: bool,
|
||||
pub rustdoc_map: bool,
|
||||
}
|
||||
|
||||
impl CliUnstable {
|
||||
|
@ -435,6 +436,7 @@ impl CliUnstable {
|
|||
"crate-versions" => self.crate_versions = parse_empty(k, v)?,
|
||||
"separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
|
||||
"multitarget" => self.multitarget = parse_empty(k, v)?,
|
||||
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
|
||||
_ => bail!("unknown `-Z` flag specified: {}", k),
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ use serde::Deserialize;
|
|||
use url::Url;
|
||||
|
||||
use self::ConfigValue as CV;
|
||||
use crate::core::compiler::rustdoc::RustdocExternMap;
|
||||
use crate::core::shell::Verbosity;
|
||||
use crate::core::{nightly_features_allowed, CliUnstable, Shell, SourceId, Workspace};
|
||||
use crate::ops;
|
||||
|
@ -172,6 +173,7 @@ pub struct Config {
|
|||
net_config: LazyCell<CargoNetConfig>,
|
||||
build_config: LazyCell<CargoBuildConfig>,
|
||||
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
|
||||
doc_extern_map: LazyCell<RustdocExternMap>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -241,6 +243,7 @@ impl Config {
|
|||
net_config: LazyCell::new(),
|
||||
build_config: LazyCell::new(),
|
||||
target_cfgs: LazyCell::new(),
|
||||
doc_extern_map: LazyCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1159,6 +1162,14 @@ impl Config {
|
|||
.try_borrow_with(|| target::load_target_cfgs(self))
|
||||
}
|
||||
|
||||
pub fn doc_extern_map(&self) -> CargoResult<&RustdocExternMap> {
|
||||
// Note: This does not support environment variables. The `Unit`
|
||||
// fundamentally does not have access to the registry name, so there is
|
||||
// nothing to query. Plumbing the name into SourceId is quite challenging.
|
||||
self.doc_extern_map
|
||||
.try_borrow_with(|| self.get::<RustdocExternMap>("doc.extern-map"))
|
||||
}
|
||||
|
||||
/// Returns the `[target]` table definition for the given target triple.
|
||||
pub fn target_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
|
||||
target::load_target_triple(self, target)
|
||||
|
|
|
@ -785,3 +785,44 @@ strip = "debuginfo"
|
|||
|
||||
Other possible values of `strip` are `none` and `symbols`. The default is
|
||||
`none`.
|
||||
|
||||
### rustdoc-map
|
||||
* Tracking Issue: TODO
|
||||
|
||||
This feature adds configuration settings that are passed to `rustdoc` so that
|
||||
it can generate links to dependencies whose documentation is hosted elsewhere
|
||||
when the dependency is not documented. First, add this to `.cargo/config`:
|
||||
|
||||
```toml
|
||||
[doc.extern-map.registries]
|
||||
crates-io = "https://docs.rs/"
|
||||
```
|
||||
|
||||
Then, when building documentation, use the following flags to cause links
|
||||
to dependencies to link to [docs.rs](https://docs.rs/):
|
||||
|
||||
```
|
||||
cargo +nightly doc --no-deps -Zrustdoc-map
|
||||
```
|
||||
|
||||
The `registries` table contains a mapping of registry name to the URL to link
|
||||
to. The URL may have the markers `{pkg_name}` and `{version}` which will get
|
||||
replaced with the corresponding values. If neither are specified, then Cargo
|
||||
defaults to appending `{pkg_name}/{version}/` to the end of the URL.
|
||||
|
||||
Another config setting is available to redirect standard library links. By
|
||||
default, rustdoc creates links to <https://doc.rust-lang.org/nightly/>. To
|
||||
change this behavior, use the `doc.extern-map.std` setting:
|
||||
|
||||
```toml
|
||||
[doc.extern-map]
|
||||
std = "local"
|
||||
```
|
||||
|
||||
A value of `"local"` means to link to the documentation found in the `rustc`
|
||||
sysroot. If you are using rustup, this documentation can be installed with
|
||||
`rustup component add rust-docs`.
|
||||
|
||||
The default value is `"remote"`.
|
||||
|
||||
The value may also take a URL for a custom location.
|
||||
|
|
|
@ -98,6 +98,7 @@ mod run;
|
|||
mod rustc;
|
||||
mod rustc_info_cache;
|
||||
mod rustdoc;
|
||||
mod rustdoc_extern_html;
|
||||
mod rustdocflags;
|
||||
mod rustflags;
|
||||
mod search;
|
||||
|
|
|
@ -0,0 +1,361 @@
|
|||
//! Tests for the -Zrustdoc-map feature.
|
||||
|
||||
use cargo_test_support::registry::Package;
|
||||
use cargo_test_support::{is_nightly, project, Project};
|
||||
|
||||
fn basic_project() -> Project {
|
||||
Package::new("bar", "1.0.0")
|
||||
.file("src/lib.rs", "pub struct Straw;")
|
||||
.publish();
|
||||
|
||||
project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bar = "1.0"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
pub fn myfun() -> Option<bar::Straw> {
|
||||
None
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
fn docs_rs(p: &Project) {
|
||||
p.change_file(
|
||||
".cargo/config",
|
||||
r#"
|
||||
[doc.extern-map.registries]
|
||||
crates-io = "https://docs.rs/"
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn ignores_on_stable() {
|
||||
// Requires -Zrustdoc-map to use.
|
||||
let p = basic_project();
|
||||
docs_rs(&p);
|
||||
p.cargo("doc -v --no-deps")
|
||||
.with_stderr_does_not_contain("[..]--extern-html-root-url[..]")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn simple() {
|
||||
// Basic test that it works with crates.io.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
let p = basic_project();
|
||||
docs_rs(&p);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains(
|
||||
"[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
|
||||
)
|
||||
.run();
|
||||
let myfun = p.read_file("target/doc/foo/fn.myfun.html");
|
||||
assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn std_docs() {
|
||||
// Mapping std docs somewhere else.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
let p = basic_project();
|
||||
p.change_file(
|
||||
".cargo/config",
|
||||
r#"
|
||||
[doc.extern-map]
|
||||
std = "local"
|
||||
"#,
|
||||
);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains("[RUNNING] `rustdoc [..]--crate-name foo [..]std=file://[..]")
|
||||
.run();
|
||||
let myfun = p.read_file("target/doc/foo/fn.myfun.html");
|
||||
assert!(myfun.contains(r#"share/doc/rust/html/core/option/enum.Option.html""#));
|
||||
|
||||
p.change_file(
|
||||
".cargo/config",
|
||||
r#"
|
||||
[doc.extern-map]
|
||||
std = "https://example.com/rust/"
|
||||
"#,
|
||||
);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains(
|
||||
"[RUNNING] `rustdoc [..]--crate-name foo [..]std=https://example.com/rust/[..]",
|
||||
)
|
||||
.run();
|
||||
let myfun = p.read_file("target/doc/foo/fn.myfun.html");
|
||||
assert!(myfun.contains(r#"href="https://example.com/rust/core/option/enum.Option.html""#));
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn renamed_dep() {
|
||||
// Handles renamed dependencies.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
Package::new("bar", "1.0.0")
|
||||
.file("src/lib.rs", "pub struct Straw;")
|
||||
.publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
groovy = { version = "1.0", package = "bar" }
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
pub fn myfun() -> Option<groovy::Straw> {
|
||||
None
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.build();
|
||||
docs_rs(&p);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains(
|
||||
"[RUNNING] `rustdoc [..]--crate-name foo [..]bar=https://docs.rs/bar/1.0.0/[..]",
|
||||
)
|
||||
.run();
|
||||
let myfun = p.read_file("target/doc/foo/fn.myfun.html");
|
||||
assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/bar/struct.Straw.html""#));
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn lib_name() {
|
||||
// Handles lib name != package name.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
Package::new("bar", "1.0.0")
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "bar"
|
||||
version = "1.0.0"
|
||||
|
||||
[lib]
|
||||
name = "rumpelstiltskin"
|
||||
"#,
|
||||
)
|
||||
.file("src/lib.rs", "pub struct Straw;")
|
||||
.publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bar = "1.0"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
pub fn myfun() -> Option<rumpelstiltskin::Straw> {
|
||||
None
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.build();
|
||||
docs_rs(&p);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains(
|
||||
"[RUNNING] `rustdoc [..]--crate-name foo [..]rumpelstiltskin=https://docs.rs/bar/1.0.0/[..]",
|
||||
)
|
||||
.run();
|
||||
let myfun = p.read_file("target/doc/foo/fn.myfun.html");
|
||||
assert!(myfun.contains(r#"href="https://docs.rs/bar/1.0.0/rumpelstiltskin/struct.Straw.html""#));
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn alt_registry() {
|
||||
// Supports other registry names.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
Package::new("bar", "1.0.0")
|
||||
.alternative(true)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
extern crate baz;
|
||||
pub struct Queen;
|
||||
pub use baz::King;
|
||||
"#,
|
||||
)
|
||||
.registry_dep("baz", "1.0")
|
||||
.publish();
|
||||
Package::new("baz", "1.0.0")
|
||||
.alternative(true)
|
||||
.file("src/lib.rs", "pub struct King;")
|
||||
.publish();
|
||||
Package::new("grimm", "1.0.0")
|
||||
.file("src/lib.rs", "pub struct Gold;")
|
||||
.publish();
|
||||
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bar = { version = "1.0", registry="alternative" }
|
||||
grimm = "1.0"
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
pub fn queen() -> bar::Queen { bar::Queen }
|
||||
pub fn king() -> bar::King { bar::King }
|
||||
pub fn gold() -> grimm::Gold { grimm::Gold }
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
".cargo/config",
|
||||
r#"
|
||||
[doc.extern-map.registries]
|
||||
alternative = "https://example.com/{pkg_name}/{version}/"
|
||||
crates-io = "https://docs.rs/"
|
||||
"#,
|
||||
)
|
||||
.build();
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains(
|
||||
"[RUNNING] `rustdoc [..]--crate-name foo \
|
||||
[..]bar=https://example.com/bar/1.0.0/[..]grimm=https://docs.rs/grimm/1.0.0/[..]",
|
||||
)
|
||||
.run();
|
||||
let queen = p.read_file("target/doc/foo/fn.queen.html");
|
||||
assert!(queen.contains(r#"href="https://example.com/bar/1.0.0/bar/struct.Queen.html""#));
|
||||
// The king example fails to link. Rustdoc seems to want the origin crate
|
||||
// name (baz) for re-exports. There are many issues in the issue tracker
|
||||
// for rustdoc re-exports, so I'm not sure, but I think this is maybe a
|
||||
// rustdoc issue. Alternatively, Cargo could provide mappings for all
|
||||
// transitive dependencies to fix this.
|
||||
let king = p.read_file("target/doc/foo/fn.king.html");
|
||||
assert!(king.contains(r#"-> King"#));
|
||||
|
||||
let gold = p.read_file("target/doc/foo/fn.gold.html");
|
||||
assert!(gold.contains(r#"href="https://docs.rs/grimm/1.0.0/grimm/struct.Gold.html""#));
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn multiple_versions() {
|
||||
// What happens when there are multiple versions.
|
||||
// NOTE: This is currently broken behavior. Rustdoc does not provide a way
|
||||
// to match renamed dependencies.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
Package::new("bar", "1.0.0")
|
||||
.file("src/lib.rs", "pub struct Spin;")
|
||||
.publish();
|
||||
Package::new("bar", "2.0.0")
|
||||
.file("src/lib.rs", "pub struct Straw;")
|
||||
.publish();
|
||||
let p = project()
|
||||
.file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bar = "1.0"
|
||||
bar2 = {version="2.0", package="bar"}
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
"
|
||||
pub fn fn1() -> bar::Spin {bar::Spin}
|
||||
pub fn fn2() -> bar2::Straw {bar2::Straw}
|
||||
",
|
||||
)
|
||||
.build();
|
||||
docs_rs(&p);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains(
|
||||
"[RUNNING] `rustdoc [..]--crate-name foo \
|
||||
[..]bar=https://docs.rs/bar/1.0.0/[..]bar=https://docs.rs/bar/2.0.0/[..]",
|
||||
)
|
||||
.run();
|
||||
let fn1 = p.read_file("target/doc/foo/fn.fn1.html");
|
||||
// This should be 1.0.0, rustdoc seems to use the last entry when there
|
||||
// are duplicates.
|
||||
assert!(fn1.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Spin.html""#));
|
||||
let fn2 = p.read_file("target/doc/foo/fn.fn2.html");
|
||||
assert!(fn2.contains(r#"href="https://docs.rs/bar/2.0.0/bar/struct.Straw.html""#));
|
||||
}
|
||||
|
||||
#[cargo_test]
|
||||
fn rebuilds_when_changing() {
|
||||
// Make sure it rebuilds if the map changes.
|
||||
if !is_nightly() {
|
||||
// --extern-html-root-url is unstable
|
||||
return;
|
||||
}
|
||||
let p = basic_project();
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_does_not_contain("[..]--extern-html-root-url[..]")
|
||||
.run();
|
||||
|
||||
docs_rs(&p);
|
||||
p.cargo("doc -v --no-deps -Zrustdoc-map")
|
||||
.masquerade_as_nightly_cargo()
|
||||
.with_stderr_contains("[..]--extern-html-root-url[..]")
|
||||
.run();
|
||||
}
|
Loading…
Reference in New Issue