From 3a92f771b22a2ac487838767f552c4dae0a52ddc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 20 Jan 2021 20:56:47 +0100 Subject: [PATCH 1/4] Add --generate-redirect-map option to replace HTML redirection file with a unique JSON map --- src/librustdoc/config.rs | 4 ++ src/librustdoc/html/render/mod.rs | 61 +++++++++++++++++++++++++------ src/librustdoc/lib.rs | 7 ++++ 3 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 1478437cefa..c9c30338367 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -265,6 +265,8 @@ crate struct RenderOptions { crate document_private: bool, /// Document items that have `doc(hidden)`. crate document_hidden: bool, + /// If `true`, generate a JSON file in the crate folder instead of HTML redirection files. + crate generate_redirect_map: bool, crate unstable_features: rustc_feature::UnstableFeatures, } @@ -586,6 +588,7 @@ impl Options { let document_private = matches.opt_present("document-private-items"); let document_hidden = matches.opt_present("document-hidden-items"); let run_check = matches.opt_present("check"); + let generate_redirect_map = matches.opt_present("generate-redirect-map"); let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format); @@ -643,6 +646,7 @@ impl Options { generate_search_filter, document_private, document_hidden, + generate_redirect_map, unstable_features: rustc_feature::UnstableFeatures::from_environment( crate_name.as_deref(), ), diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 7f122bb8cb5..cd115eef314 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -111,6 +111,10 @@ crate struct Context<'tcx> { /// real location of an item. This is used to allow external links to /// publicly reused items to redirect to the right location. crate render_redirect_pages: bool, + /// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set + /// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of + /// the crate. + crate redirections: Option>>>, /// The map used to ensure all generated 'id=' attributes are unique. id_map: Rc>, /// Tracks section IDs for `Deref` targets so they match in both the main @@ -405,6 +409,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { static_root_path, generate_search_filter, unstable_features, + generate_redirect_map, .. } = options; @@ -510,6 +515,11 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { all: Rc::new(RefCell::new(AllTypes::new())), errors: Rc::new(receiver), cache: Rc::new(cache), + redirections: if generate_redirect_map { + Some(Rc::new(RefCell::new(FxHashMap::default()))) + } else { + None + }, }; CURRENT_DEPTH.with(|s| s.set(0)); @@ -588,6 +598,15 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { &style_files, ); self.shared.fs.write(&settings_file, v.as_bytes())?; + if let Some(redirections) = self.redirections.take() { + if !redirections.borrow().is_empty() { + let redirect_map_path = + self.dst.join(&*krate.name.as_str()).join("redirect-map.json"); + let paths = serde_json::to_string(&*redirections.borrow()).unwrap(); + self.shared.ensure_dir(&self.dst.join(&*krate.name.as_str()))?; + self.shared.fs.write(&redirect_map_path, paths.as_bytes())?; + } + } // Flush pending errors. Arc::get_mut(&mut self.shared).unwrap().fs.close(); @@ -664,9 +683,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { if !buf.is_empty() { let name = item.name.as_ref().unwrap(); let item_type = item.type_(); - let file_name = &item_path(item_type, &name.as_str()); + let file_name = item_path(item_type, &name.as_str()); self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join(file_name); + let joint_dst = self.dst.join(&file_name); self.shared.fs.write(&joint_dst, buf.as_bytes())?; if !self.render_redirect_pages { @@ -676,9 +695,17 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // to the new one (without). if item_type == ItemType::Macro { let redir_name = format!("{}.{}!.html", item_type, name); - let redir_dst = self.dst.join(redir_name); - let v = layout::redirect(file_name); - self.shared.fs.write(&redir_dst, v.as_bytes())?; + if let Some(ref redirections) = self.redirections { + let crate_name = &self.shared.layout.krate; + redirections.borrow_mut().insert( + format!("{}/{}", crate_name, redir_name), + format!("{}/{}", crate_name, file_name), + ); + } else { + let v = layout::redirect(&file_name); + let redir_dst = self.dst.join(redir_name); + self.shared.fs.write(&redir_dst, v.as_bytes())?; + } } } Ok(()) @@ -1588,17 +1615,27 @@ impl Context<'_> { &self.shared.style_files, ) } else { - let mut url = self.root_path(); if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) { + let mut path = String::new(); for name in &names[..names.len() - 1] { - url.push_str(name); - url.push('/'); + path.push_str(name); + path.push('/'); + } + path.push_str(&item_path(ty, names.last().unwrap())); + match self.redirections { + Some(ref redirections) => { + let mut current_path = String::new(); + for name in &self.current { + current_path.push_str(name); + current_path.push('/'); + } + current_path.push_str(&item_path(ty, names.last().unwrap())); + redirections.borrow_mut().insert(current_path, path); + } + None => return layout::redirect(&format!("{}{}", self.root_path(), path)), } - url.push_str(&item_path(ty, names.last().unwrap())); - layout::redirect(&url) - } else { - String::new() } + String::new() } } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d7978c4a022..afacf2dc29e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -423,6 +423,13 @@ fn opts() -> Vec { o.optopt("", "test-builder", "The rustc-like binary to use as the test builder", "PATH") }), unstable("check", |o| o.optflag("", "check", "Run rustdoc checks")), + unstable("generate-redirect-map", |o| { + o.optflag( + "", + "generate-redirect-map", + "Generate JSON file at the top level instead of generating HTML redirection files", + ) + }), ] } From 2b59e7667de6f63391d9cc390c6e9cd3559380e1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 20 Jan 2021 20:57:13 +0100 Subject: [PATCH 2/4] Add tests for --generate-redirect-map option --- src/test/rustdoc/redirect-map-empty.rs | 6 ++++++ src/test/rustdoc/redirect-map.rs | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/test/rustdoc/redirect-map-empty.rs create mode 100644 src/test/rustdoc/redirect-map.rs diff --git a/src/test/rustdoc/redirect-map-empty.rs b/src/test/rustdoc/redirect-map-empty.rs new file mode 100644 index 00000000000..e9d021e0fa8 --- /dev/null +++ b/src/test/rustdoc/redirect-map-empty.rs @@ -0,0 +1,6 @@ +// compile-flags: -Z unstable-options --generate-redirect-map + +#![crate_name = "foo"] + +// @!has foo/redirect-map.json +pub struct Foo; diff --git a/src/test/rustdoc/redirect-map.rs b/src/test/rustdoc/redirect-map.rs new file mode 100644 index 00000000000..b7f16b64e38 --- /dev/null +++ b/src/test/rustdoc/redirect-map.rs @@ -0,0 +1,23 @@ +// compile-flags: -Z unstable-options --generate-redirect-map + +#![crate_name = "foo"] + +// @!has foo/private/struct.Quz.html +// @!has foo/hidden/struct.Bar.html +// @has foo/redirect-map.json +pub use private::Quz; +pub use hidden::Bar; + +mod private { + pub struct Quz; +} + +#[doc(hidden)] +pub mod hidden { + pub struct Bar; +} + +#[macro_export] +macro_rules! foo { + () => {} +} From 1a7126b30b4014144b0b4285070b0bac42ca0ed1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 15 Feb 2021 21:45:38 +0100 Subject: [PATCH 3/4] Add full test for rustdoc map file --- .../rustdoc-map-file/Makefile | 5 +++ .../rustdoc-map-file/expected.json | 5 +++ .../run-make-fulldeps/rustdoc-map-file/foo.rs | 16 ++++++++ .../rustdoc-map-file/validate_json.py | 41 +++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 src/test/run-make-fulldeps/rustdoc-map-file/Makefile create mode 100644 src/test/run-make-fulldeps/rustdoc-map-file/expected.json create mode 100644 src/test/run-make-fulldeps/rustdoc-map-file/foo.rs create mode 100755 src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/Makefile b/src/test/run-make-fulldeps/rustdoc-map-file/Makefile new file mode 100644 index 00000000000..ce977fa0cea --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-map-file/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: + $(RUSTDOC) -Z unstable-options --generate-redirect-map foo.rs -o "$(TMPDIR)/out" + "$(PYTHON)" validate_json.py "$(TMPDIR)/out" diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/expected.json b/src/test/run-make-fulldeps/rustdoc-map-file/expected.json new file mode 100644 index 00000000000..6b1ccbeac30 --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-map-file/expected.json @@ -0,0 +1,5 @@ +{ + "foo/macro.foo!.html": "foo/macro.foo.html", + "foo/private/struct.Quz.html": "foo/struct.Quz.html", + "foo/hidden/struct.Bar.html": "foo/struct.Bar.html" +} diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/foo.rs b/src/test/run-make-fulldeps/rustdoc-map-file/foo.rs new file mode 100644 index 00000000000..e12b9d2292c --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-map-file/foo.rs @@ -0,0 +1,16 @@ +pub use private::Quz; +pub use hidden::Bar; + +mod private { + pub struct Quz; +} + +#[doc(hidden)] +pub mod hidden { + pub struct Bar; +} + +#[macro_export] +macro_rules! foo { + () => {} +} diff --git a/src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py b/src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py new file mode 100755 index 00000000000..5c14c90b70d --- /dev/null +++ b/src/test/run-make-fulldeps/rustdoc-map-file/validate_json.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +import os +import sys +import json + + +def find_redirect_map_file(folder, errors): + for root, dirs, files in os.walk(folder): + for name in files: + if not name.endswith("redirect-map.json"): + continue + with open(os.path.join(root, name)) as f: + data = json.load(f) + with open("expected.json") as f: + expected = json.load(f) + for key in expected: + if expected[key] != data.get(key): + errors.append("Expected `{}` for key `{}`, found: `{}`".format( + expected[key], key, data.get(key))) + else: + del data[key] + for key in data: + errors.append("Extra data not expected: key: `{}`, data: `{}`".format( + key, data[key])) + return True + return False + + +if len(sys.argv) != 2: + print("Expected doc directory to check!") + sys.exit(1) + +errors = [] +if not find_redirect_map_file(sys.argv[1], errors): + print("Didn't find the map file in `{}`...".format(sys.argv[1])) + sys.exit(1) +for err in errors: + print("=> {}".format(err)) +if len(errors) != 0: + sys.exit(1) From 41fc58b7b56cbc1bf70b8928ba053110fa65a34b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 23 Feb 2021 11:26:12 +0100 Subject: [PATCH 4/4] Fix nits --- src/librustdoc/html/render/mod.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index cd115eef314..437da4a75c0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -515,11 +515,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { all: Rc::new(RefCell::new(AllTypes::new())), errors: Rc::new(receiver), cache: Rc::new(cache), - redirections: if generate_redirect_map { - Some(Rc::new(RefCell::new(FxHashMap::default()))) - } else { - None - }, + redirections: if generate_redirect_map { Some(Default::default()) } else { None }, }; CURRENT_DEPTH.with(|s| s.set(0)); @@ -683,9 +679,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { if !buf.is_empty() { let name = item.name.as_ref().unwrap(); let item_type = item.type_(); - let file_name = item_path(item_type, &name.as_str()); + let file_name = &item_path(item_type, &name.as_str()); self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join(&file_name); + let joint_dst = self.dst.join(file_name); self.shared.fs.write(&joint_dst, buf.as_bytes())?; if !self.render_redirect_pages { @@ -702,7 +698,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { format!("{}/{}", crate_name, file_name), ); } else { - let v = layout::redirect(&file_name); + let v = layout::redirect(file_name); let redir_dst = self.dst.join(redir_name); self.shared.fs.write(&redir_dst, v.as_bytes())?; }