1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::core::compiler::RustcTargetData;
use crate::core::resolver::{features::RequestedFeatures, HasDevUnits, ResolveOpts};
use crate::core::{Shell, Workspace};
use crate::ops;
use crate::util::CargoResult;
use std::collections::HashMap;
use std::path::Path;
use std::process::Command;
#[derive(Debug)]
pub struct DocOptions {
pub open_result: bool,
pub compile_opts: ops::CompileOptions,
}
pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
let specs = options.compile_opts.spec.to_package_id_specs(ws)?;
let opts = ResolveOpts::new(
true,
RequestedFeatures::from_command_line(
&options.compile_opts.features,
options.compile_opts.all_features,
!options.compile_opts.no_default_features,
),
);
let target_data = RustcTargetData::new(ws, &options.compile_opts.build_config.requested_kinds)?;
let ws_resolve = ops::resolve_ws_with_opts(
ws,
&target_data,
&options.compile_opts.build_config.requested_kinds,
&opts,
&specs,
HasDevUnits::No,
crate::core::resolver::features::ForceAllTargets::No,
)?;
let ids = ws_resolve.targeted_resolve.specs_to_ids(&specs)?;
let pkgs = ws_resolve.pkg_set.get_many(ids)?;
let mut lib_names = HashMap::new();
let mut bin_names = HashMap::new();
let mut names = Vec::new();
for package in &pkgs {
for target in package.targets().iter().filter(|t| t.documented()) {
if target.is_lib() {
if let Some(prev) = lib_names.insert(target.crate_name(), package) {
anyhow::bail!(
"The library `{}` is specified by packages `{}` and \
`{}` but can only be documented once. Consider renaming \
or marking one of the targets as `doc = false`.",
target.crate_name(),
prev,
package
);
}
} else if let Some(prev) = bin_names.insert(target.crate_name(), package) {
anyhow::bail!(
"The binary `{}` is specified by packages `{}` and \
`{}` but can be documented only once. Consider renaming \
or marking one of the targets as `doc = false`.",
target.crate_name(),
prev,
package
);
}
names.push(target.crate_name());
}
}
let open_kind = if options.open_result {
Some(options.compile_opts.build_config.single_requested_kind()?)
} else {
None
};
let compilation = ops::compile(ws, &options.compile_opts)?;
if let Some(kind) = open_kind {
let name = match names.first() {
Some(s) => s.to_string(),
None => return Ok(()),
};
let path = compilation.root_output[&kind]
.with_file_name("doc")
.join(&name)
.join("index.html");
if path.exists() {
let mut shell = ws.config().shell();
shell.status("Opening", path.display())?;
open_docs(&path, &mut shell)?;
}
}
Ok(())
}
fn open_docs(path: &Path, shell: &mut Shell) -> CargoResult<()> {
match std::env::var_os("BROWSER") {
Some(browser) => {
if let Err(e) = Command::new(&browser).arg(path).status() {
shell.warn(format!(
"Couldn't open docs with {}: {}",
browser.to_string_lossy(),
e
))?;
}
}
None => {
if let Err(e) = opener::open(&path) {
let e = e.into();
crate::display_warning_with_error("couldn't open docs", &e, shell);
}
}
};
Ok(())
}