Skip to content

Commit ff8cf3f

Browse files
committed
Add unstable --output-format option to "rustdoc"
We achieved this by: * Handle `--output-format` argument, accepting `html` or `json` * If `json` is passed, we append the following in `prepare_rustdoc`: 1. `-Zunstable-options` 2. `--output-format` Fixes #12103 Signed-off-by: Charalampos Mitrodimas <[email protected]>
1 parent 0c98d6e commit ff8cf3f

File tree

17 files changed

+250
-17
lines changed

17 files changed

+250
-17
lines changed

src/bin/cargo/commands/doc.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,15 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
4949
let ws = args.workspace(config)?;
5050
let mode = CompileMode::Doc {
5151
deps: !args.flag("no-deps"),
52+
json: false,
5253
};
5354
let mut compile_opts =
5455
args.compile_options(config, mode, Some(&ws), ProfileChecking::Custom)?;
5556
compile_opts.rustdoc_document_private_items = args.flag("document-private-items");
5657

5758
let doc_opts = DocOptions {
5859
open_result: args.flag("open"),
60+
output_format: ops::OutputFormat::Html,
5961
compile_opts,
6062
};
6163
ops::doc(&ws, &doc_opts)?;

src/bin/cargo/commands/rustdoc.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use cargo::ops::{self, DocOptions};
1+
use std::str::FromStr;
2+
3+
use cargo::ops::{self, DocOptions, OutputFormat};
24

35
use crate::command_prelude::*;
46

@@ -38,6 +40,11 @@ pub fn cli() -> Command {
3840
.arg_profile("Build artifacts with the specified profile")
3941
.arg_target_triple("Build for the target triple")
4042
.arg_target_dir()
43+
.arg(
44+
opt("output-format", "The output type to write (unstable)")
45+
.value_name("FMT")
46+
.value_parser(OutputFormat::POSSIBLE_VALUES),
47+
)
4148
.arg_unit_graph()
4249
.arg_timings()
4350
.arg_manifest_path()
@@ -48,20 +55,37 @@ pub fn cli() -> Command {
4855

4956
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
5057
let ws = args.workspace(config)?;
58+
let output_format = if let Some(output_format) = args._value_of("output-format") {
59+
config
60+
.cli_unstable()
61+
.fail_if_stable_opt("--output-format", 12103)?;
62+
OutputFormat::from_str(output_format).unwrap()
63+
} else {
64+
OutputFormat::Html
65+
};
66+
67+
let open_result = args.flag("open");
68+
5169
let mut compile_opts = args.compile_options_for_single_package(
5270
config,
53-
CompileMode::Doc { deps: false },
71+
CompileMode::Doc {
72+
deps: false,
73+
json: matches!(output_format, OutputFormat::Json),
74+
},
5475
Some(&ws),
5576
ProfileChecking::Custom,
5677
)?;
5778
let target_args = values(args, "args");
79+
5880
compile_opts.target_rustdoc_args = if target_args.is_empty() {
5981
None
6082
} else {
6183
Some(target_args)
6284
};
85+
6386
let doc_opts = DocOptions {
64-
open_result: args.flag("open"),
87+
open_result,
88+
output_format,
6589
compile_opts,
6690
};
6791
ops::doc(&ws, &doc_opts)?;

src/cargo/core/compiler/build_config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,10 @@ pub enum CompileMode {
176176
/// allows some de-duping of Units to occur.
177177
Bench,
178178
/// A target that will be documented with `rustdoc`.
179+
179180
/// If `deps` is true, then it will also document all dependencies.
180-
Doc { deps: bool },
181+
/// if `json` is true, the documentation output is in json format.
182+
Doc { deps: bool, json: bool },
181183
/// A target that will be tested with `rustdoc`.
182184
Doctest,
183185
/// An example or library that will be scraped for function calls by `rustdoc`.

src/cargo/core/compiler/context/compilation_files.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -435,11 +435,16 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
435435
bcx: &BuildContext<'a, 'cfg>,
436436
) -> CargoResult<Arc<Vec<OutputFile>>> {
437437
let ret = match unit.mode {
438-
CompileMode::Doc { .. } => {
439-
let path = self
440-
.out_dir(unit)
441-
.join(unit.target.crate_name())
442-
.join("index.html");
438+
CompileMode::Doc { json, .. } => {
439+
let path = if json {
440+
self.out_dir(unit)
441+
.join(format!("{}.json", unit.target.crate_name()))
442+
} else {
443+
self.out_dir(unit)
444+
.join(unit.target.crate_name())
445+
.join("index.html")
446+
};
447+
443448
vec![OutputFile {
444449
path,
445450
hardlink: None,

src/cargo/core/compiler/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,8 @@ fn prepare_rustdoc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuil
762762
build_deps_args(&mut rustdoc, cx, unit)?;
763763
rustdoc::add_root_urls(cx, unit, &mut rustdoc)?;
764764

765+
rustdoc::add_output_format(cx, unit, &mut rustdoc)?;
766+
765767
rustdoc.args(bcx.rustdocflags_args(unit));
766768

767769
if !crate_version_flag_already_present(&rustdoc) {

src/cargo/core/compiler/rustdoc.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use std::fmt;
1111
use std::hash;
1212
use url::Url;
1313

14+
use super::CompileMode;
15+
1416
const DOCS_RS_URL: &'static str = "https://docs.rs/";
1517

1618
/// Mode used for `std`. This is for unstable feature [`-Zrustdoc-map`][1].
@@ -204,6 +206,29 @@ pub fn add_root_urls(
204206
Ok(())
205207
}
206208

209+
/// Adds unstable flag [`--output-format`][1] to the given `rustdoc`
210+
/// invocation. This is for unstable feature [`-Zunstable-features`].
211+
///
212+
/// [1]: https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html?highlight=output-format#-w--output-format-output-format
213+
pub fn add_output_format(
214+
cx: &Context<'_, '_>,
215+
unit: &Unit,
216+
rustdoc: &mut ProcessBuilder,
217+
) -> CargoResult<()> {
218+
let config = cx.bcx.config;
219+
if !config.cli_unstable().unstable_options {
220+
tracing::debug!("`unstable-options` is ignored, required -Zunstable-options flag");
221+
return Ok(());
222+
}
223+
224+
if let CompileMode::Doc { json: true, .. } = unit.mode {
225+
rustdoc.arg("-Zunstable-options");
226+
rustdoc.arg("--output-format=json");
227+
}
228+
229+
Ok(())
230+
}
231+
207232
/// Indicates whether a target should have examples scraped from it by rustdoc.
208233
/// Configured within Cargo.toml and only for unstable feature
209234
/// [`-Zrustdoc-scrape-examples`][1].

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ fn compute_deps_doc(
627627
)?;
628628
ret.push(lib_unit_dep);
629629
if dep_lib.documented() {
630-
if let CompileMode::Doc { deps: true } = unit.mode {
630+
if let CompileMode::Doc { deps: true, .. } = unit.mode {
631631
// Document this lib as well.
632632
let doc_unit_dep = new_unit_dep(
633633
state,

src/cargo/ops/cargo_compile/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ pub fn create_bcx<'a, 'cfg>(
420420

421421
// TODO: In theory, Cargo should also dedupe the roots, but I'm uncertain
422422
// what heuristics to use in that case.
423-
if build_config.mode == (CompileMode::Doc { deps: true }) {
423+
if matches!(build_config.mode, CompileMode::Doc { deps: true, .. }) {
424424
remove_duplicate_doc(build_config, &units, &mut unit_graph);
425425
}
426426

src/cargo/ops/cargo_doc.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,50 @@ use crate::core::{Shell, Workspace};
22
use crate::ops;
33
use crate::util::config::{Config, PathAndArgs};
44
use crate::util::CargoResult;
5+
use anyhow::{bail, Error};
56
use std::path::Path;
67
use std::path::PathBuf;
78
use std::process::Command;
9+
use std::str::FromStr;
10+
11+
/// Format of rustdoc [`--output-format`][1].
12+
///
13+
/// [1]: https://doc.rust-lang.org/nightly/rustdoc/unstable-features.html#-w--output-format-output-format
14+
#[derive(Debug, Default, Clone)]
15+
pub enum OutputFormat {
16+
#[default]
17+
Html,
18+
Json,
19+
}
20+
21+
impl OutputFormat {
22+
pub const POSSIBLE_VALUES: [&'static str; 2] = ["html", "json"];
23+
}
24+
25+
impl FromStr for OutputFormat {
26+
// bail! error instead of string error like impl FromStr for Edition {
27+
type Err = Error;
28+
29+
fn from_str(s: &str) -> Result<Self, Self::Err> {
30+
match s {
31+
"json" => Ok(OutputFormat::Json),
32+
"html" => Ok(OutputFormat::Html),
33+
_ => bail!(
34+
"supported values for --output-format are `json` and `html`, \
35+
but `{}` is unknown",
36+
s
37+
),
38+
}
39+
}
40+
}
841

942
/// Strongly typed options for the `cargo doc` command.
1043
#[derive(Debug)]
1144
pub struct DocOptions {
1245
/// Whether to attempt to open the browser after compiling the docs
1346
pub open_result: bool,
47+
/// Same as `rustdoc --output-format`
48+
pub output_format: OutputFormat,
1449
/// Options to pass through to the compiler
1550
pub compile_opts: ops::CompileOptions,
1651
}
@@ -25,10 +60,18 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
2560
.get(0)
2661
.ok_or_else(|| anyhow::anyhow!("no crates with documentation"))?;
2762
let kind = options.compile_opts.build_config.single_requested_kind()?;
28-
let path = compilation.root_output[&kind]
29-
.with_file_name("doc")
30-
.join(&name)
31-
.join("index.html");
63+
64+
let path = if matches!(options.output_format, OutputFormat::Json) {
65+
compilation.root_output[&kind]
66+
.with_file_name("doc")
67+
.join(format!("{}.json", &name))
68+
} else {
69+
compilation.root_output[&kind]
70+
.with_file_name("doc")
71+
.join(&name)
72+
.join("index.html")
73+
};
74+
3275
if path.exists() {
3376
let config_browser = {
3477
let cfg: Option<PathAndArgs> = ws.config().get("doc.browser")?;

src/cargo/ops/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ pub use self::cargo_compile::{
55
compile, compile_with_exec, compile_ws, create_bcx, print, resolve_all_features, CompileOptions,
66
};
77
pub use self::cargo_compile::{CompileFilter, FilterRule, LibRule, Packages};
8-
pub use self::cargo_doc::{doc, DocOptions};
8+
pub use self::cargo_doc::{doc, DocOptions, OutputFormat};
99
pub use self::cargo_fetch::{fetch, FetchOptions};
1010
pub use self::cargo_generate_lockfile::generate_lockfile;
1111
pub use self::cargo_generate_lockfile::update_lockfile;

src/doc/man/cargo-rustdoc.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ if its name is the same as the lib target. Binaries are skipped if they have
102102
{{#options}}
103103
{{> options-jobs }}
104104
{{> options-keep-going }}
105+
{{> options-output-format }}
105106
{{/options}}
106107

107108
{{> section-environment }}

src/doc/man/generated_txt/cargo-rustdoc.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,18 @@ OPTIONS
327327
--keep-going would definitely run both builds, even if the one run
328328
first fails.
329329

330+
--output-format
331+
The output type for the documentation emitted. Valid values:
332+
333+
o html (default): Emit the documentation in HTML format.
334+
335+
o json: Emit the documentation in the experimental JSON format
336+
<https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types>.
337+
338+
This option is only available on the nightly channel
339+
<https://doc.rust-lang.org/book/appendix-07-nightly-rust.html> and
340+
requires the -Z unstable-options flag to enable.
341+
330342
ENVIRONMENT
331343
See the reference
332344
<https://doc.rust-lang.org/cargo/reference/environment-variables.html>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{{#option "`--output-format`"}}
2+
The output type for the documentation emitted. Valid values:
3+
4+
* `html` (default): Emit the documentation in HTML format.
5+
* `json`: Emit the documentation in the [experimental JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types).
6+
7+
This option is only available on the [nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
8+
and requires the `-Z unstable-options` flag to enable.
9+
{{/option}}

src/doc/src/commands/cargo-rustdoc.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,15 @@ one that succeeds (depending on which one of the two builds Cargo picked to run
371371
first), whereas <code>cargo rustdoc -j1 --keep-going</code> would definitely run both
372372
builds, even if the one run first fails.</dd>
373373

374+
<dt class="option-term" id="option-cargo-rustdoc---output-format"><a class="option-anchor" href="#option-cargo-rustdoc---output-format"></a><code>--output-format</code></dt>
375+
<dd class="option-desc">The output type for the documentation emitted. Valid values:</p>
376+
<ul>
377+
<li><code>html</code> (default): Emit the documentation in HTML format.</li>
378+
<li><code>json</code>: Emit the documentation in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types">experimental JSON format</a>.</li>
379+
</ul>
380+
<p>This option is only available on the <a href="https://doc.rust-lang.org/book/appendix-07-nightly-rust.html">nightly channel</a>
381+
and requires the <code>-Z unstable-options</code> flag to enable.</dd>
382+
374383
</dl>
375384

376385
## ENVIRONMENT

src/etc/man/cargo-rustdoc.1

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,22 @@ one that succeeds (depending on which one of the two builds Cargo picked to run
395395
first), whereas \fBcargo rustdoc \-j1 \-\-keep\-going\fR would definitely run both
396396
builds, even if the one run first fails.
397397
.RE
398+
.sp
399+
\fB\-\-output\-format\fR
400+
.RS 4
401+
The output type for the documentation emitted. Valid values:
402+
.sp
403+
.RS 4
404+
\h'-04'\(bu\h'+02'\fBhtml\fR (default): Emit the documentation in HTML format.
405+
.RE
406+
.sp
407+
.RS 4
408+
\h'-04'\(bu\h'+02'\fBjson\fR: Emit the documentation in the \fIexperimental JSON format\fR <https://doc.rust\-lang.org/nightly/nightly\-rustc/rustdoc_json_types>\&.
409+
.RE
410+
.sp
411+
This option is only available on the \fInightly channel\fR <https://doc.rust\-lang.org/book/appendix\-07\-nightly\-rust.html>
412+
and requires the \fB\-Z unstable\-options\fR flag to enable.
413+
.RE
398414
.SH "ENVIRONMENT"
399415
See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/environment\-variables.html> for
400416
details on environment variables that Cargo reads.

tests/testsuite/cargo_rustdoc/help/stdout.log

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Build a package's documentation, using specified custom flags.
22

3-
Usage: cargo[EXE] rustdoc [OPTIONS] [ARGS]...
3+
Usage: cargo rustdoc [OPTIONS] [ARGS]...
44

55
Arguments:
66
[ARGS]... Extra rustdoc flags
@@ -9,6 +9,7 @@ Options:
99
--open Opens the docs in a browser after the operation
1010
--ignore-rust-version Ignore `rust-version` specification in packages
1111
--message-format <FMT> Error format
12+
--output-format <FMT> The output type to write (unstable) [possible values: html, json]
1213
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
1314
-q, --quiet Do not print cargo log messages
1415
--color <WHEN> Coloring: auto, always, never

0 commit comments

Comments
 (0)