Skip to content

Commit

Permalink
Rollup merge of rust-lang#128963 - GuillaumeGomez:output-to-stdout, r…
Browse files Browse the repository at this point in the history
…=aDotInTheVoid

Add possibility to generate rustdoc JSON output to stdout

Fixes rust-lang#127165.

I think it's likely common to want to get rustdoc json output directly instead of reading it from a file so I added this option to allow it. It's unstable and only works with `--output-format=json`.

r? `@aDotInTheVoid`
  • Loading branch information
matthiaskrgr authored Aug 14, 2024
2 parents cd1b42c + 63ab7b5 commit 6c242a0
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 21 deletions.
3 changes: 3 additions & 0 deletions src/doc/rustdoc/src/unstable-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ pub fn no_documentation() {}

Note that the third item is the crate root, which in this case is undocumented.

If you want the JSON output to be displayed on `stdout` instead of having a file generated, you can
use `-o -`.

### `-w`/`--output-format`: output format

`--output-format json` emits documentation in the experimental
Expand Down
15 changes: 10 additions & 5 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ pub(crate) struct RenderOptions {
pub(crate) no_emit_shared: bool,
/// If `true`, HTML source code pages won't be generated.
pub(crate) html_no_source: bool,
/// This field is only used for the JSON output. If it's set to true, no file will be created
/// and content will be displayed in stdout directly.
pub(crate) output_to_stdout: bool,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -548,16 +551,17 @@ impl Options {
dcx.fatal("the `--test` flag must be passed to enable `--no-run`");
}

let mut output_to_stdout = false;
let test_builder_wrappers =
matches.opt_strs("test-builder-wrapper").iter().map(PathBuf::from).collect();
let out_dir = matches.opt_str("out-dir").map(|s| PathBuf::from(&s));
let output = matches.opt_str("output").map(|s| PathBuf::from(&s));
let output = match (out_dir, output) {
let output = match (matches.opt_str("out-dir"), matches.opt_str("output")) {
(Some(_), Some(_)) => {
dcx.fatal("cannot use both 'out-dir' and 'output' at once");
}
(Some(out_dir), None) => out_dir,
(None, Some(output)) => output,
(Some(out_dir), None) | (None, Some(out_dir)) => {
output_to_stdout = out_dir == "-";
PathBuf::from(out_dir)
}
(None, None) => PathBuf::from("doc"),
};

Expand Down Expand Up @@ -818,6 +822,7 @@ impl Options {
call_locations,
no_emit_shared: false,
html_no_source,
output_to_stdout,
};
Some((options, render_options))
}
Expand Down
47 changes: 31 additions & 16 deletions src/librustdoc/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod import_finder;

use std::cell::RefCell;
use std::fs::{create_dir_all, File};
use std::io::{BufWriter, Write};
use std::io::{stdout, BufWriter, Write};
use std::path::PathBuf;
use std::rc::Rc;

Expand Down Expand Up @@ -37,7 +37,7 @@ pub(crate) struct JsonRenderer<'tcx> {
/// level field of the JSON blob.
index: Rc<RefCell<FxHashMap<types::Id, types::Item>>>,
/// The directory where the blob will be written to.
out_path: PathBuf,
out_path: Option<PathBuf>,
cache: Rc<Cache>,
imported_items: DefIdSet,
}
Expand Down Expand Up @@ -97,6 +97,20 @@ impl<'tcx> JsonRenderer<'tcx> {
})
.unwrap_or_default()
}

fn write<T: Write>(
&self,
output: types::Crate,
mut writer: BufWriter<T>,
path: &str,
) -> Result<(), Error> {
self.tcx
.sess
.time("rustdoc_json_serialization", || serde_json::ser::to_writer(&mut writer, &output))
.unwrap();
try_err!(writer.flush(), path);
Ok(())
}
}

impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
Expand All @@ -120,7 +134,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
JsonRenderer {
tcx,
index: Rc::new(RefCell::new(FxHashMap::default())),
out_path: options.output,
out_path: if options.output_to_stdout { None } else { Some(options.output) },
cache: Rc::new(cache),
imported_items,
},
Expand Down Expand Up @@ -264,20 +278,21 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
.collect(),
format_version: types::FORMAT_VERSION,
};
let out_dir = self.out_path.clone();
try_err!(create_dir_all(&out_dir), out_dir);
if let Some(ref out_path) = self.out_path {
let out_dir = out_path.clone();
try_err!(create_dir_all(&out_dir), out_dir);

let mut p = out_dir;
p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
p.set_extension("json");
let mut file = BufWriter::new(try_err!(File::create(&p), p));
self.tcx
.sess
.time("rustdoc_json_serialization", || serde_json::ser::to_writer(&mut file, &output))
.unwrap();
try_err!(file.flush(), p);

Ok(())
let mut p = out_dir;
p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
p.set_extension("json");
self.write(
output,
BufWriter::new(try_err!(File::create(&p), p)),
&p.display().to_string(),
)
} else {
self.write(output, BufWriter::new(stdout()), "<stdout>")
}
}

fn cache(&self) -> &Cache {
Expand Down
15 changes: 15 additions & 0 deletions src/tools/run-make-support/src/path_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,18 @@ pub fn has_suffix<P: AsRef<Path>>(path: P, suffix: &str) -> bool {
pub fn filename_contains<P: AsRef<Path>>(path: P, needle: &str) -> bool {
path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(needle))
}

/// Helper for reading entries in a given directory and its children.
pub fn read_dir_entries_recursive<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F) {
fn read_dir_entries_recursive_inner<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, callback: &mut F) {
for entry in rfs::read_dir(dir) {
let path = entry.unwrap().path();
callback(&path);
if path.is_dir() {
read_dir_entries_recursive_inner(path, callback);
}
}
}

read_dir_entries_recursive_inner(dir, &mut callback);
}
1 change: 1 addition & 0 deletions tests/run-make/rustdoc-output-stdout/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct Foo;
25 changes: 25 additions & 0 deletions tests/run-make/rustdoc-output-stdout/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This test verifies that rustdoc `-o -` prints JSON on stdout and doesn't generate
// a JSON file.

use std::path::PathBuf;

use run_make_support::path_helpers::{cwd, has_extension, read_dir_entries_recursive};
use run_make_support::rustdoc;

fn main() {
// First we check that we generate the JSON in the stdout.
rustdoc()
.input("foo.rs")
.output("-")
.arg("-Zunstable-options")
.output_format("json")
.run()
.assert_stdout_contains("{\"");

// Then we check it didn't generate any JSON file.
read_dir_entries_recursive(cwd(), |path| {
if path.is_file() && has_extension(path, "json") {
panic!("Found a JSON file {path:?}");
}
});
}

0 comments on commit 6c242a0

Please sign in to comment.