mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Add support for rustdoc root URL mappings.
This commit is contained in:
parent
ff9126d0d2
commit
e0f9643b0f
@ -73,6 +73,7 @@
|
|||||||
//! mtime of sources | ✓[^3] |
|
//! mtime of sources | ✓[^3] |
|
||||||
//! RUSTFLAGS/RUSTDOCFLAGS | ✓ |
|
//! RUSTFLAGS/RUSTDOCFLAGS | ✓ |
|
||||||
//! LTO flags | ✓ |
|
//! LTO flags | ✓ |
|
||||||
|
//! config settings[^5] | ✓ |
|
||||||
//! is_std | | ✓
|
//! is_std | | ✓
|
||||||
//!
|
//!
|
||||||
//! [^1]: Build script and bin dependencies are not included.
|
//! [^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
|
//! [^4]: `__CARGO_DEFAULT_LIB_METADATA` is set by rustbuild to embed the
|
||||||
//! release channel (bootstrap/stable/beta/nightly) in libstd.
|
//! 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
|
//! 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,
|
//! 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,
|
//! 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
|
/// "description", which are exposed as environment variables during
|
||||||
/// compilation.
|
/// compilation.
|
||||||
metadata: u64,
|
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
|
/// Description of whether the filesystem status for this unit is up to date
|
||||||
/// or should be considered stale.
|
/// or should be considered stale.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@ -746,6 +752,7 @@ impl Fingerprint {
|
|||||||
memoized_hash: Mutex::new(None),
|
memoized_hash: Mutex::new(None),
|
||||||
rustflags: Vec::new(),
|
rustflags: Vec::new(),
|
||||||
metadata: 0,
|
metadata: 0,
|
||||||
|
config: 0,
|
||||||
fs_status: FsStatus::Stale,
|
fs_status: FsStatus::Stale,
|
||||||
outputs: Vec::new(),
|
outputs: Vec::new(),
|
||||||
}
|
}
|
||||||
@ -806,6 +813,9 @@ impl Fingerprint {
|
|||||||
if self.metadata != old.metadata {
|
if self.metadata != old.metadata {
|
||||||
bail!("metadata changed")
|
bail!("metadata changed")
|
||||||
}
|
}
|
||||||
|
if self.config != old.config {
|
||||||
|
bail!("configuration settings have changed")
|
||||||
|
}
|
||||||
let my_local = self.local.lock().unwrap();
|
let my_local = self.local.lock().unwrap();
|
||||||
let old_local = old.local.lock().unwrap();
|
let old_local = old.local.lock().unwrap();
|
||||||
if my_local.len() != old_local.len() {
|
if my_local.len() != old_local.len() {
|
||||||
@ -1040,12 +1050,13 @@ impl hash::Hash for Fingerprint {
|
|||||||
ref deps,
|
ref deps,
|
||||||
ref local,
|
ref local,
|
||||||
metadata,
|
metadata,
|
||||||
|
config,
|
||||||
ref rustflags,
|
ref rustflags,
|
||||||
..
|
..
|
||||||
} = *self;
|
} = *self;
|
||||||
let local = local.lock().unwrap();
|
let local = local.lock().unwrap();
|
||||||
(
|
(
|
||||||
rustc, features, target, path, profile, &*local, metadata, rustflags,
|
rustc, features, target, path, profile, &*local, metadata, config, rustflags,
|
||||||
)
|
)
|
||||||
.hash(h);
|
.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.
|
// Include metadata since it is exposed as environment variables.
|
||||||
let m = unit.pkg.manifest().metadata();
|
let m = unit.pkg.manifest().metadata();
|
||||||
let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository));
|
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 {
|
Ok(Fingerprint {
|
||||||
rustc: util::hash_u64(&cx.bcx.rustc().verbose_version),
|
rustc: util::hash_u64(&cx.bcx.rustc().verbose_version),
|
||||||
target: util::hash_u64(&unit.target),
|
target: util::hash_u64(&unit.target),
|
||||||
@ -1264,6 +1283,7 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
|
|||||||
local: Mutex::new(local),
|
local: Mutex::new(local),
|
||||||
memoized_hash: Mutex::new(None),
|
memoized_hash: Mutex::new(None),
|
||||||
metadata,
|
metadata,
|
||||||
|
config,
|
||||||
rustflags: extra_flags,
|
rustflags: extra_flags,
|
||||||
fs_status: FsStatus::Stale,
|
fs_status: FsStatus::Stale,
|
||||||
outputs,
|
outputs,
|
||||||
|
@ -13,6 +13,7 @@ mod layout;
|
|||||||
mod links;
|
mod links;
|
||||||
mod lto;
|
mod lto;
|
||||||
mod output_depinfo;
|
mod output_depinfo;
|
||||||
|
pub mod rustdoc;
|
||||||
pub mod standard_lib;
|
pub mod standard_lib;
|
||||||
mod timings;
|
mod timings;
|
||||||
mod unit;
|
mod unit;
|
||||||
@ -570,6 +571,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
build_deps_args(&mut rustdoc, cx, unit)?;
|
build_deps_args(&mut rustdoc, cx, unit)?;
|
||||||
|
rustdoc::add_root_urls(cx, unit, &mut rustdoc)?;
|
||||||
|
|
||||||
rustdoc.args(bcx.rustdocflags_args(unit));
|
rustdoc.args(bcx.rustdocflags_args(unit));
|
||||||
|
|
||||||
|
172
src/cargo/core/compiler/rustdoc.rs
Normal file
172
src/cargo/core/compiler/rustdoc.rs
Normal file
@ -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 crate_versions: bool,
|
||||||
pub separate_nightlies: bool,
|
pub separate_nightlies: bool,
|
||||||
pub multitarget: bool,
|
pub multitarget: bool,
|
||||||
|
pub rustdoc_map: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CliUnstable {
|
impl CliUnstable {
|
||||||
@ -435,6 +436,7 @@ impl CliUnstable {
|
|||||||
"crate-versions" => self.crate_versions = parse_empty(k, v)?,
|
"crate-versions" => self.crate_versions = parse_empty(k, v)?,
|
||||||
"separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
|
"separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?,
|
||||||
"multitarget" => self.multitarget = 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),
|
_ => bail!("unknown `-Z` flag specified: {}", k),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ use serde::Deserialize;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use self::ConfigValue as CV;
|
use self::ConfigValue as CV;
|
||||||
|
use crate::core::compiler::rustdoc::RustdocExternMap;
|
||||||
use crate::core::shell::Verbosity;
|
use crate::core::shell::Verbosity;
|
||||||
use crate::core::{nightly_features_allowed, CliUnstable, Shell, SourceId, Workspace};
|
use crate::core::{nightly_features_allowed, CliUnstable, Shell, SourceId, Workspace};
|
||||||
use crate::ops;
|
use crate::ops;
|
||||||
@ -172,6 +173,7 @@ pub struct Config {
|
|||||||
net_config: LazyCell<CargoNetConfig>,
|
net_config: LazyCell<CargoNetConfig>,
|
||||||
build_config: LazyCell<CargoBuildConfig>,
|
build_config: LazyCell<CargoBuildConfig>,
|
||||||
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
|
target_cfgs: LazyCell<Vec<(String, TargetCfgConfig)>>,
|
||||||
|
doc_extern_map: LazyCell<RustdocExternMap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -241,6 +243,7 @@ impl Config {
|
|||||||
net_config: LazyCell::new(),
|
net_config: LazyCell::new(),
|
||||||
build_config: LazyCell::new(),
|
build_config: LazyCell::new(),
|
||||||
target_cfgs: 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))
|
.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.
|
/// Returns the `[target]` table definition for the given target triple.
|
||||||
pub fn target_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
|
pub fn target_cfg_triple(&self, target: &str) -> CargoResult<TargetConfig> {
|
||||||
target::load_target_triple(self, target)
|
target::load_target_triple(self, target)
|
||||||
|
@ -785,3 +785,44 @@ strip = "debuginfo"
|
|||||||
|
|
||||||
Other possible values of `strip` are `none` and `symbols`. The default is
|
Other possible values of `strip` are `none` and `symbols`. The default is
|
||||||
`none`.
|
`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;
|
||||||
mod rustc_info_cache;
|
mod rustc_info_cache;
|
||||||
mod rustdoc;
|
mod rustdoc;
|
||||||
|
mod rustdoc_extern_html;
|
||||||
mod rustdocflags;
|
mod rustdocflags;
|
||||||
mod rustflags;
|
mod rustflags;
|
||||||
mod search;
|
mod search;
|
||||||
|
361
tests/testsuite/rustdoc_extern_html.rs
Normal file
361
tests/testsuite/rustdoc_extern_html.rs
Normal file
@ -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…
x
Reference in New Issue
Block a user