From 7c89cc4a6f68a9c544ff1b000c8f7ef1c3535278 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 25 Mar 2021 11:40:32 -0400 Subject: [PATCH 1/5] Add SharedResource abstraction and use it in write_shared This cleans up the code quite a bit, and also makes the next commit much easier. --- src/librustdoc/html/render/context.rs | 13 +- src/librustdoc/html/render/write_shared.rs | 217 +++++++++++---------- 2 files changed, 115 insertions(+), 115 deletions(-) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 64d413a5f3119..468bd9997a64a 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -79,17 +79,6 @@ crate struct Context<'tcx> { rustc_data_structures::static_assert_size!(Context<'_>, 152); impl<'tcx> Context<'tcx> { - pub(super) fn path(&self, filename: &str) -> PathBuf { - // We use splitn vs Path::extension here because we might get a filename - // like `style.min.css` and we want to process that into - // `style-suffix.min.css`. Path::extension would just return `css` - // which would result in `style.min-suffix.css` which isn't what we - // want. - let (base, ext) = filename.split_once('.').unwrap(); - let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext); - self.dst.join(&filename) - } - pub(super) fn tcx(&self) -> TyCtxt<'tcx> { self.shared.tcx } @@ -487,7 +476,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { |buf: &mut Buffer| all.print(buf), &self.shared.style_files, ); - self.shared.fs.write(&final_file, v.as_bytes())?; + self.shared.fs.write(final_file, v.as_bytes())?; // Generating settings page. page.title = "Rustdoc settings"; diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 501d8e8e02e84..2ab423c238c40 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -14,7 +14,7 @@ use serde::Serialize; use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS}; use crate::clean::Crate; use crate::config::RenderOptions; -use crate::docfs::{DocFS, PathError}; +use crate::docfs::PathError; use crate::error::Error; use crate::formats::FormatRenderer; use crate::html::{layout, static_files}; @@ -40,6 +40,81 @@ crate static FILES_UNVERSIONED: Lazy> = Lazy::new(|| { } }); +enum SharedResource<'a> { + /// This file will never change, no matter what toolchain is used to build it. + /// + /// It does not have a resource suffix. + Unversioned { name: &'a str }, + /// This file may change depending on the toolchain. + /// + /// It has a resource suffix. + ToolchainSpecific { basename: &'a str }, + /// This file may change for any crate within a build. + /// + /// This differs from normal crate-specific files because it has a resource suffix. + CrateSpecific { basename: &'a str }, +} + +impl SharedResource<'_> { + fn extension(&self) -> Option<&OsStr> { + use SharedResource::*; + match self { + Unversioned { name } + | ToolchainSpecific { basename: name } + | CrateSpecific { basename: name } => Path::new(name).extension(), + } + } + + fn path(&self, cx: &Context<'_>) -> PathBuf { + match self { + SharedResource::Unversioned { name } => cx.dst.join(name), + SharedResource::ToolchainSpecific { basename } => cx.suffix_path(basename), + SharedResource::CrateSpecific { basename } => cx.suffix_path(basename), + } + } +} + +impl Context<'_> { + fn suffix_path(&self, filename: &str) -> PathBuf { + // We use splitn vs Path::extension here because we might get a filename + // like `style.min.css` and we want to process that into + // `style-suffix.min.css`. Path::extension would just return `css` + // which would result in `style.min-suffix.css` which isn't what we + // want. + let (base, ext) = filename.split_once('.').unwrap(); + let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext); + self.dst.join(&filename) + } + + fn write_shared>(&self, resource: SharedResource<'_>, contents: C) -> Result<(), Error> + { + self.shared.fs.write(resource.path(self), contents) + } + + fn write_minify( + &self, + resource: SharedResource<'_>, + contents: &str, + minify: bool, + ) -> Result<(), Error> { + let tmp; + let contents = if minify { + tmp = if resource.extension() == Some(&OsStr::new("css")) { + minifier::css::minify(contents).map_err(|e| { + Error::new(format!("failed to minify CSS file: {}", e), resource.path(self)) + })? + } else { + minifier::js::minify(contents) + }; + tmp.as_bytes() + } else { + contents.as_bytes() + }; + + self.write_shared(resource, contents) + } +} + pub(super) fn write_shared( cx: &Context<'_>, krate: &Crate, @@ -52,27 +127,22 @@ pub(super) fn write_shared( let lock_file = cx.dst.join(".lock"); let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); + // The weird `: &_` is to work around a borrowck bug: https://github.com/rust-lang/rust/issues/41078#issuecomment-293646723 + let write_minify = |p, c: &_| { + cx.write_minify( + SharedResource::ToolchainSpecific { basename: p }, + c, + options.enable_minification, + ) + }; + let write_toolchain = + |p: &_, c: &_| cx.write_shared(SharedResource::ToolchainSpecific { basename: p }, c); + // Add all the static files. These may already exist, but we just // overwrite them anyway to make sure that they're fresh and up-to-date. - - write_minify( - &cx.shared.fs, - cx.path("rustdoc.css"), - static_files::RUSTDOC_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.css"), - static_files::SETTINGS_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("noscript.css"), - static_files::NOSCRIPT_CSS, - options.enable_minification, - )?; + write_minify("rustdoc.css", static_files::RUSTDOC_CSS)?; + write_minify("settings.css", static_files::SETTINGS_CSS)?; + write_minify("noscript.css", static_files::NOSCRIPT_CSS)?; // To avoid "light.css" to be overwritten, we'll first run over the received themes and only // then we'll run over the "official" styles. @@ -85,106 +155,66 @@ pub(super) fn write_shared( // Handle the official themes match theme { - "light" => write_minify( - &cx.shared.fs, - cx.path("light.css"), - static_files::themes::LIGHT, - options.enable_minification, - )?, - "dark" => write_minify( - &cx.shared.fs, - cx.path("dark.css"), - static_files::themes::DARK, - options.enable_minification, - )?, - "ayu" => write_minify( - &cx.shared.fs, - cx.path("ayu.css"), - static_files::themes::AYU, - options.enable_minification, - )?, + "light" => write_minify("light.css", static_files::themes::LIGHT)?, + "dark" => write_minify("dark.css", static_files::themes::DARK)?, + "ayu" => write_minify("ayu.css", static_files::themes::AYU)?, _ => { // Handle added third-party themes let content = try_err!(fs::read(&entry.path), &entry.path); - cx.shared - .fs - .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?; + // This is not exactly right: if compiled a second time with the same toolchain but different CLI args, the file could be different. + // But docs.rs doesn't use this, so hopefully the issue doesn't come up. + write_toolchain(&format!("{}.{}", theme, extension), content.as_slice())?; } }; themes.insert(theme.to_owned()); } - let write = |p, c| cx.shared.fs.write(p, c); if (*cx.shared).layout.logo.is_empty() { - write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?; + write_toolchain("rust-logo.png", static_files::RUST_LOGO)?; } if (*cx.shared).layout.favicon.is_empty() { - write(cx.path("favicon.svg"), static_files::RUST_FAVICON_SVG)?; - write(cx.path("favicon-16x16.png"), static_files::RUST_FAVICON_PNG_16)?; - write(cx.path("favicon-32x32.png"), static_files::RUST_FAVICON_PNG_32)?; + write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?; + write_toolchain("favicon-16x16.png", static_files::RUST_FAVICON_PNG_16)?; + write_toolchain("favicon-32x32.png", static_files::RUST_FAVICON_PNG_32)?; } - write(cx.path("brush.svg"), static_files::BRUSH_SVG)?; - write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?; - write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?; + write_toolchain("brush.svg", static_files::BRUSH_SVG)?; + write_toolchain("wheel.svg", static_files::WHEEL_SVG)?; + write_toolchain("down-arrow.svg", static_files::DOWN_ARROW_SVG)?; let mut themes: Vec<&String> = themes.iter().collect(); themes.sort(); write_minify( - &cx.shared.fs, - cx.path("main.js"), + "main.js", &static_files::MAIN_JS.replace( "/* INSERT THEMES HERE */", &format!(" = {}", serde_json::to_string(&themes).unwrap()), ), - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.js"), - static_files::SETTINGS_JS, - options.enable_minification, )?; + write_minify("settings.js", static_files::SETTINGS_JS)?; if cx.shared.include_sources { - write_minify( - &cx.shared.fs, - cx.path("source-script.js"), - static_files::sidebar::SOURCE_SCRIPT, - options.enable_minification, - )?; + write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT)?; } { write_minify( - &cx.shared.fs, - cx.path("storage.js"), + "storage.js", &format!( "var resourcesSuffix = \"{}\";{}", cx.shared.resource_suffix, static_files::STORAGE_JS ), - options.enable_minification, )?; } if let Some(ref css) = cx.shared.layout.css_file_extension { - let out = cx.path("theme.css"); let buffer = try_err!(fs::read_to_string(css), css); - if !options.enable_minification { - cx.shared.fs.write(&out, &buffer)?; - } else { - write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?; - } + write_minify("theme.css", &buffer)?; } - write_minify( - &cx.shared.fs, - cx.path("normalize.css"), - static_files::NORMALIZE_CSS, - options.enable_minification, - )?; - for (file, contents) in &*FILES_UNVERSIONED { - write(cx.dst.join(file), contents)?; + write_minify("normalize.css", static_files::NORMALIZE_CSS)?; + for (name, contents) in &*FILES_UNVERSIONED { + cx.write_shared(SharedResource::Unversioned { name }, contents)?; } fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> { @@ -324,7 +354,7 @@ pub(super) fn write_shared( "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", all_sources.join("\n") ); - cx.shared.fs.write(&dst, v.as_bytes())?; + cx.write_shared(SharedResource::CrateSpecific { basename: "source-files.js" }, v)?; } // Update the search index and crate list. @@ -341,13 +371,12 @@ pub(super) fn write_shared( let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); v.push_str(&all_indexes.join(",\\\n")); v.push_str("\\\n}');\ninitSearch(searchIndex);"); - cx.shared.fs.write(&dst, &v)?; + cx.write_shared(SharedResource::CrateSpecific { basename: "search-index.js" }, v)?; } - let crate_list_dst = cx.dst.join(&format!("crates{}.js", cx.shared.resource_suffix)); let crate_list = format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(",")); - cx.shared.fs.write(&crate_list_dst, &crate_list)?; + cx.write_shared(SharedResource::CrateSpecific { basename: "crates.js" }, crate_list)?; if options.enable_index_page { if let Some(index_page) = options.index_page.clone() { @@ -481,21 +510,3 @@ pub(super) fn write_shared( } Ok(()) } - -fn write_minify( - fs: &DocFS, - dst: PathBuf, - contents: &str, - enable_minification: bool, -) -> Result<(), Error> { - if enable_minification { - if dst.extension() == Some(&OsStr::new("css")) { - let res = try_none!(minifier::css::minify(contents).ok(), &dst); - fs.write(dst, res.as_bytes()) - } else { - fs.write(dst, minifier::js::minify(contents).as_bytes()) - } - } else { - fs.write(dst, contents.as_bytes()) - } -} From f77ebd4ffaea7fc5af49425cafefe141e7458cc3 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 25 Mar 2021 12:46:35 -0400 Subject: [PATCH 2/5] Add unstable option to only emit shared/crate-specific files The intended use case is for docs.rs, which can now copy exactly the files it cares about, rather than having to guess based on whether they have a resource suffix or not. In particular, some files have a resource suffix but cannot be shared between crates: https://github.com/rust-lang/docs.rs/pull/1312#issuecomment-798783688 The end goal is to fix https://github.com/rust-lang/docs.rs/issues/1327 by reverting https://github.com/rust-lang/docs.rs/pull/1324. This obsoletes `--print=unversioned-files`, which I plan to remove as soon as docs.rs stops using it. --- src/librustdoc/config.rs | 43 ++++++++++++++++++ src/librustdoc/formats/renderer.rs | 5 +++ src/librustdoc/html/render/context.rs | 5 ++- src/librustdoc/html/render/write_shared.rs | 47 +++++++++++++++----- src/librustdoc/lib.rs | 8 ++++ src/test/run-make/emit-shared-files/Makefile | 32 +++++++++++++ src/test/run-make/emit-shared-files/x.rs | 1 + 7 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/test/run-make/emit-shared-files/Makefile create mode 100644 src/test/run-make/emit-shared-files/x.rs diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index ecb6378f31fb4..a7a2db6210005 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -3,6 +3,7 @@ use std::convert::TryFrom; use std::ffi::OsStr; use std::fmt; use std::path::PathBuf; +use std::str::FromStr; use rustc_data_structures::fx::FxHashMap; use rustc_session::config::{self, parse_crate_types_from_list, parse_externs, CrateType}; @@ -266,6 +267,34 @@ crate struct RenderOptions { /// 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, + crate emit: Vec, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +crate enum EmitType { + Unversioned, + Toolchain, + CrateSpecific, +} + +impl FromStr for EmitType { + type Err = (); + + fn from_str(s: &str) -> Result { + use EmitType::*; + match s { + "unversioned-shared-resources" => Ok(Unversioned), + "toolchain-shared-resources" => Ok(Toolchain), + "crate-specific" => Ok(CrateSpecific), + _ => Err(()), + } + } +} + +impl RenderOptions { + crate fn should_emit_crate(&self) -> bool { + self.emit.is_empty() || self.emit.contains(&EmitType::CrateSpecific) + } } impl Options { @@ -334,6 +363,19 @@ impl Options { // check for deprecated options check_deprecated_options(&matches, &diag); + let mut emit = Vec::new(); + for list in matches.opt_strs("emit") { + for kind in list.split(',') { + match kind.parse() { + Ok(kind) => emit.push(kind), + Err(()) => { + diag.err(&format!("unrecognized emission type: {}", kind)); + return Err(1); + } + } + } + } + let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let paths = theme::load_css_paths(static_files::themes::LIGHT.as_bytes()); @@ -641,6 +683,7 @@ impl Options { unstable_features: rustc_feature::UnstableFeatures::from_environment( crate_name.as_deref(), ), + emit, }, crate_name, output_format, diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 9dcef3a20d6c0..f0b390add73d5 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -58,10 +58,15 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>( ) -> Result<(), Error> { let prof = &tcx.sess.prof; + let emit_crate = options.should_emit_crate(); let (mut format_renderer, krate) = prof .extra_verbose_generic_activity("create_renderer", T::descr()) .run(|| T::init(krate, options, edition, cache, tcx))?; + if !emit_crate { + return Ok(()); + } + // Render the crate documentation let crate_name = krate.name; let mut work = vec![(format_renderer.make_child_renderer(), krate.module)]; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 468bd9997a64a..17cedeb5a511a 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -288,6 +288,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { ) -> Result<(Self, clean::Crate), Error> { // need to save a copy of the options for rendering the index page let md_opts = options.clone(); + let emit_crate = options.should_emit_crate(); let RenderOptions { output, external_html, @@ -393,7 +394,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let dst = output; scx.ensure_dir(&dst)?; - krate = sources::render(&dst, &mut scx, krate)?; + if emit_crate { + krate = sources::render(&dst, &mut scx, krate)?; + } // Build our search index let index = build_index(&krate, &mut cache, tcx); diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 2ab423c238c40..af346bb82256e 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -13,7 +13,7 @@ use serde::Serialize; use super::{collect_paths_for_type, ensure_trailing_slash, Context, BASIC_KEYWORDS}; use crate::clean::Crate; -use crate::config::RenderOptions; +use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; use crate::error::Error; use crate::formats::FormatRenderer; @@ -72,6 +72,18 @@ impl SharedResource<'_> { SharedResource::CrateSpecific { basename } => cx.suffix_path(basename), } } + + fn should_emit(&self, emit: &[EmitType]) -> bool { + if emit.is_empty() { + return true; + } + let kind = match self { + SharedResource::Unversioned { .. } => EmitType::Unversioned, + SharedResource::ToolchainSpecific { .. } => EmitType::Toolchain, + SharedResource::CrateSpecific { .. } => EmitType::CrateSpecific, + }; + emit.contains(&kind) + } } impl Context<'_> { @@ -86,9 +98,17 @@ impl Context<'_> { self.dst.join(&filename) } - fn write_shared>(&self, resource: SharedResource<'_>, contents: C) -> Result<(), Error> - { - self.shared.fs.write(resource.path(self), contents) + fn write_shared>( + &self, + resource: SharedResource<'_>, + contents: C, + emit: &[EmitType], + ) -> Result<(), Error> { + if resource.should_emit(emit) { + self.shared.fs.write(resource.path(self), contents) + } else { + Ok(()) + } } fn write_minify( @@ -96,6 +116,7 @@ impl Context<'_> { resource: SharedResource<'_>, contents: &str, minify: bool, + emit: &[EmitType], ) -> Result<(), Error> { let tmp; let contents = if minify { @@ -111,7 +132,7 @@ impl Context<'_> { contents.as_bytes() }; - self.write_shared(resource, contents) + self.write_shared(resource, contents, emit) } } @@ -133,10 +154,14 @@ pub(super) fn write_shared( SharedResource::ToolchainSpecific { basename: p }, c, options.enable_minification, + &options.emit, ) }; - let write_toolchain = - |p: &_, c: &_| cx.write_shared(SharedResource::ToolchainSpecific { basename: p }, c); + let write_toolchain = |p: &_, c: &_| { + cx.write_shared(SharedResource::ToolchainSpecific { basename: p }, c, &options.emit) + }; + let write_crate = + |p, c: &_| cx.write_shared(SharedResource::CrateSpecific { basename: p }, c, &options.emit); // Add all the static files. These may already exist, but we just // overwrite them anyway to make sure that they're fresh and up-to-date. @@ -214,7 +239,7 @@ pub(super) fn write_shared( } write_minify("normalize.css", static_files::NORMALIZE_CSS)?; for (name, contents) in &*FILES_UNVERSIONED { - cx.write_shared(SharedResource::Unversioned { name }, contents)?; + cx.write_shared(SharedResource::Unversioned { name }, contents, &options.emit)?; } fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec, Vec)> { @@ -354,7 +379,7 @@ pub(super) fn write_shared( "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", all_sources.join("\n") ); - cx.write_shared(SharedResource::CrateSpecific { basename: "source-files.js" }, v)?; + write_crate("source-files.js", &v)?; } // Update the search index and crate list. @@ -371,12 +396,12 @@ pub(super) fn write_shared( let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); v.push_str(&all_indexes.join(",\\\n")); v.push_str("\\\n}');\ninitSearch(searchIndex);"); - cx.write_shared(SharedResource::CrateSpecific { basename: "search-index.js" }, v)?; + write_crate("search-index.js", &v)?; } let crate_list = format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(",")); - cx.write_shared(SharedResource::CrateSpecific { basename: "crates.js" }, crate_list)?; + write_crate("crates.js", &crate_list)?; if options.enable_index_page { if let Some(index_page) = options.index_page.clone() { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index dabc21e3a447c..ebdd56611db71 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -527,6 +527,14 @@ fn opts() -> Vec { unstable("print", |o| { o.optmulti("", "print", "Rustdoc information to print on stdout", "[unversioned-files]") }), + unstable("emit", |o| { + o.optmulti( + "", + "emit", + "Comma separated list of types of output for rustdoc to emit", + "[unversioned-shared-resources,toolchain-shared-resources,crate-specific]", + ) + }), ] } diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile new file mode 100644 index 0000000000000..06358f6eb2c98 --- /dev/null +++ b/src/test/run-make/emit-shared-files/Makefile @@ -0,0 +1,32 @@ +-include ../../run-make-fulldeps/tools.mk + +CRATE_ONLY = $(TMPDIR)/crate-only +TOOLCHAIN_ONLY = $(TMPDIR)/toolchain-only +ALL_SHARED = $(TMPDIR)/all-shared + +all: crate-only toolchain-only all-shared + +crate-only: + $(RUSTDOC) -Z unstable-options --emit=crate-specific --output $(CRATE_ONLY) --resource-suffix=-xxx x.rs + [ -e $(CRATE_ONLY)/search-index-xxx.js ] + [ -e $(CRATE_ONLY)/settings.html ] + [ -e $(CRATE_ONLY)/x/all.html ] + [ -e $(CRATE_ONLY)/x/index.html ] + ! [ -e $(CRATE_ONLY)/storage-xxx.js ] + ! [ -e $(CRATE_ONLY)/SourceSerifPro-It.ttf.woff ] + +toolchain-only: + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx x.rs + [ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/SourceSerifPro-It.ttf.woff ] + ! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/x/index.html ] + +all-shared: + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx x.rs + [ -e $(ALL_SHARED)/storage-xxx.js ] + [ -e $(ALL_SHARED)/SourceSerifPro-It.ttf.woff ] + ! [ -e $(ALL_SHARED)/search-index-xxx.js ] + ! [ -e $(ALL_SHARED)/settings.html ] + ! [ -e $(ALL_SHARED)/x ] + ! [ -e $(ALL_SHARED)/src ] diff --git a/src/test/run-make/emit-shared-files/x.rs b/src/test/run-make/emit-shared-files/x.rs new file mode 100644 index 0000000000000..5df7576133a68 --- /dev/null +++ b/src/test/run-make/emit-shared-files/x.rs @@ -0,0 +1 @@ +// nothing to see here From d4f3f91c48dd244357a439a234a44d5f0e99f014 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 31 Mar 2021 11:13:51 -0400 Subject: [PATCH 3/5] Enforce that Toolchain files are static and Crate files are dynamic This also changes custom themes from Toolchain to Crate files. --- src/librustdoc/html/render/write_shared.rs | 63 +++++++++++--------- src/test/run-make/emit-shared-files/Makefile | 6 +- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index af346bb82256e..b19b29d2fa0d8 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -44,11 +44,11 @@ enum SharedResource<'a> { /// This file will never change, no matter what toolchain is used to build it. /// /// It does not have a resource suffix. - Unversioned { name: &'a str }, + Unversioned { name: &'static str }, /// This file may change depending on the toolchain. /// /// It has a resource suffix. - ToolchainSpecific { basename: &'a str }, + ToolchainSpecific { basename: &'static str }, /// This file may change for any crate within a build. /// /// This differs from normal crate-specific files because it has a resource suffix. @@ -157,11 +157,16 @@ pub(super) fn write_shared( &options.emit, ) }; - let write_toolchain = |p: &_, c: &_| { + // Toolchain resources should never be dynamic. + let write_toolchain = |p: &'static _, c: &'static _| { cx.write_shared(SharedResource::ToolchainSpecific { basename: p }, c, &options.emit) }; - let write_crate = - |p, c: &_| cx.write_shared(SharedResource::CrateSpecific { basename: p }, c, &options.emit); + + // Crate resources should always be dynamic. + let write_crate = |p: &_, make_content: &dyn Fn() -> Result, Error>| { + let content = make_content()?; + cx.write_shared(SharedResource::CrateSpecific { basename: p }, content, &options.emit) + }; // Add all the static files. These may already exist, but we just // overwrite them anyway to make sure that they're fresh and up-to-date. @@ -185,10 +190,8 @@ pub(super) fn write_shared( "ayu" => write_minify("ayu.css", static_files::themes::AYU)?, _ => { // Handle added third-party themes - let content = try_err!(fs::read(&entry.path), &entry.path); - // This is not exactly right: if compiled a second time with the same toolchain but different CLI args, the file could be different. - // But docs.rs doesn't use this, so hopefully the issue doesn't come up. - write_toolchain(&format!("{}.{}", theme, extension), content.as_slice())?; + let filename = format!("{}.{}", theme, extension); + write_crate(&filename, &|| Ok(try_err!(fs::read(&entry.path), &entry.path)))?; } }; @@ -367,19 +370,22 @@ pub(super) fn write_shared( } let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); - let (mut all_sources, _krates) = - try_err!(collect(&dst, &krate.name.as_str(), "sourcesIndex"), &dst); - all_sources.push(format!( - "sourcesIndex[\"{}\"] = {};", - &krate.name, - hierarchy.to_json_string() - )); - all_sources.sort(); - let v = format!( - "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", - all_sources.join("\n") - ); - write_crate("source-files.js", &v)?; + let make_sources = || { + let (mut all_sources, _krates) = + try_err!(collect(&dst, &krate.name.as_str(), "sourcesIndex"), &dst); + all_sources.push(format!( + "sourcesIndex[\"{}\"] = {};", + &krate.name, + hierarchy.to_json_string() + )); + all_sources.sort(); + Ok(format!( + "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", + all_sources.join("\n") + ) + .into_bytes()) + }; + write_crate("source-files.js", &make_sources)?; } // Update the search index and crate list. @@ -392,16 +398,17 @@ pub(super) fn write_shared( // Sort the indexes by crate so the file will be generated identically even // with rustdoc running in parallel. all_indexes.sort(); - { + write_crate("search-index.js", &|| { let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); v.push_str(&all_indexes.join(",\\\n")); v.push_str("\\\n}');\ninitSearch(searchIndex);"); - write_crate("search-index.js", &v)?; - } + Ok(v.into_bytes()) + })?; - let crate_list = - format!("window.ALL_CRATES = [{}];", krates.iter().map(|k| format!("\"{}\"", k)).join(",")); - write_crate("crates.js", &crate_list)?; + write_crate("crates.js", &|| { + let krates = krates.iter().map(|k| format!("\"{}\"", k)).join(","); + Ok(format!("window.ALL_CRATES = [{}];", krates).into_bytes()) + })?; if options.enable_index_page { if let Some(index_page) = options.index_page.clone() { diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile index 06358f6eb2c98..952c3aaf5602d 100644 --- a/src/test/run-make/emit-shared-files/Makefile +++ b/src/test/run-make/emit-shared-files/Makefile @@ -7,17 +7,20 @@ ALL_SHARED = $(TMPDIR)/all-shared all: crate-only toolchain-only all-shared crate-only: - $(RUSTDOC) -Z unstable-options --emit=crate-specific --output $(CRATE_ONLY) --resource-suffix=-xxx x.rs + $(RUSTDOC) -Z unstable-options --emit=crate-specific --output $(CRATE_ONLY) --resource-suffix=-xxx --theme y.css x.rs [ -e $(CRATE_ONLY)/search-index-xxx.js ] [ -e $(CRATE_ONLY)/settings.html ] [ -e $(CRATE_ONLY)/x/all.html ] [ -e $(CRATE_ONLY)/x/index.html ] + # FIXME: this probably shouldn't have a suffix + [ -e $(CRATE_ONLY)/y-xxx.css ] ! [ -e $(CRATE_ONLY)/storage-xxx.js ] ! [ -e $(CRATE_ONLY)/SourceSerifPro-It.ttf.woff ] toolchain-only: $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx x.rs [ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ] ! [ -e $(TOOLCHAIN_ONLY)/SourceSerifPro-It.ttf.woff ] ! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ] ! [ -e $(TOOLCHAIN_ONLY)/x/index.html ] @@ -26,6 +29,7 @@ all-shared: $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx x.rs [ -e $(ALL_SHARED)/storage-xxx.js ] [ -e $(ALL_SHARED)/SourceSerifPro-It.ttf.woff ] + ! [ -e $(ALL_SHARED)/y-xxx.css ] ! [ -e $(ALL_SHARED)/search-index-xxx.js ] ! [ -e $(ALL_SHARED)/settings.html ] ! [ -e $(ALL_SHARED)/x ] From 1086d9b7b58fea8f15b1f10bc2e527b6fcb0e382 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 31 Mar 2021 11:35:57 -0400 Subject: [PATCH 4/5] Rename CrateSpecific -> InvocationSpecific --- src/librustdoc/config.rs | 6 +++--- src/librustdoc/html/render/write_shared.rs | 14 ++++++------- src/librustdoc/lib.rs | 2 +- src/test/run-make/emit-shared-files/Makefile | 22 ++++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index a7a2db6210005..246e0ebbb2ba0 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -274,7 +274,7 @@ crate struct RenderOptions { crate enum EmitType { Unversioned, Toolchain, - CrateSpecific, + InvocationSpecific, } impl FromStr for EmitType { @@ -285,7 +285,7 @@ impl FromStr for EmitType { match s { "unversioned-shared-resources" => Ok(Unversioned), "toolchain-shared-resources" => Ok(Toolchain), - "crate-specific" => Ok(CrateSpecific), + "invocation-specific" => Ok(InvocationSpecific), _ => Err(()), } } @@ -293,7 +293,7 @@ impl FromStr for EmitType { impl RenderOptions { crate fn should_emit_crate(&self) -> bool { - self.emit.is_empty() || self.emit.contains(&EmitType::CrateSpecific) + self.emit.is_empty() || self.emit.contains(&EmitType::InvocationSpecific) } } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index b19b29d2fa0d8..34994097812c2 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -49,10 +49,10 @@ enum SharedResource<'a> { /// /// It has a resource suffix. ToolchainSpecific { basename: &'static str }, - /// This file may change for any crate within a build. + /// This file may change for any crate within a build, or based on the CLI arguments. /// - /// This differs from normal crate-specific files because it has a resource suffix. - CrateSpecific { basename: &'a str }, + /// This differs from normal invocation-specific files because it has a resource suffix. + InvocationSpecific { basename: &'a str }, } impl SharedResource<'_> { @@ -61,7 +61,7 @@ impl SharedResource<'_> { match self { Unversioned { name } | ToolchainSpecific { basename: name } - | CrateSpecific { basename: name } => Path::new(name).extension(), + | InvocationSpecific { basename: name } => Path::new(name).extension(), } } @@ -69,7 +69,7 @@ impl SharedResource<'_> { match self { SharedResource::Unversioned { name } => cx.dst.join(name), SharedResource::ToolchainSpecific { basename } => cx.suffix_path(basename), - SharedResource::CrateSpecific { basename } => cx.suffix_path(basename), + SharedResource::InvocationSpecific { basename } => cx.suffix_path(basename), } } @@ -80,7 +80,7 @@ impl SharedResource<'_> { let kind = match self { SharedResource::Unversioned { .. } => EmitType::Unversioned, SharedResource::ToolchainSpecific { .. } => EmitType::Toolchain, - SharedResource::CrateSpecific { .. } => EmitType::CrateSpecific, + SharedResource::InvocationSpecific { .. } => EmitType::InvocationSpecific, }; emit.contains(&kind) } @@ -165,7 +165,7 @@ pub(super) fn write_shared( // Crate resources should always be dynamic. let write_crate = |p: &_, make_content: &dyn Fn() -> Result, Error>| { let content = make_content()?; - cx.write_shared(SharedResource::CrateSpecific { basename: p }, content, &options.emit) + cx.write_shared(SharedResource::InvocationSpecific { basename: p }, content, &options.emit) }; // Add all the static files. These may already exist, but we just diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ebdd56611db71..54a6fc625a615 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -532,7 +532,7 @@ fn opts() -> Vec { "", "emit", "Comma separated list of types of output for rustdoc to emit", - "[unversioned-shared-resources,toolchain-shared-resources,crate-specific]", + "[unversioned-shared-resources,toolchain-shared-resources,invocation-specific]", ) }), ] diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile index 952c3aaf5602d..66a36dae9d370 100644 --- a/src/test/run-make/emit-shared-files/Makefile +++ b/src/test/run-make/emit-shared-files/Makefile @@ -1,21 +1,21 @@ -include ../../run-make-fulldeps/tools.mk -CRATE_ONLY = $(TMPDIR)/crate-only +INVOCATION_ONLY = $(TMPDIR)/invocation-only TOOLCHAIN_ONLY = $(TMPDIR)/toolchain-only ALL_SHARED = $(TMPDIR)/all-shared -all: crate-only toolchain-only all-shared +all: invocation-only toolchain-only all-shared -crate-only: - $(RUSTDOC) -Z unstable-options --emit=crate-specific --output $(CRATE_ONLY) --resource-suffix=-xxx --theme y.css x.rs - [ -e $(CRATE_ONLY)/search-index-xxx.js ] - [ -e $(CRATE_ONLY)/settings.html ] - [ -e $(CRATE_ONLY)/x/all.html ] - [ -e $(CRATE_ONLY)/x/index.html ] +invocation-only: + $(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css x.rs + [ -e $(INVOCATION_ONLY)/search-index-xxx.js ] + [ -e $(INVOCATION_ONLY)/settings.html ] + [ -e $(INVOCATION_ONLY)/x/all.html ] + [ -e $(INVOCATION_ONLY)/x/index.html ] # FIXME: this probably shouldn't have a suffix - [ -e $(CRATE_ONLY)/y-xxx.css ] - ! [ -e $(CRATE_ONLY)/storage-xxx.js ] - ! [ -e $(CRATE_ONLY)/SourceSerifPro-It.ttf.woff ] + [ -e $(INVOCATION_ONLY)/y-xxx.css ] + ! [ -e $(INVOCATION_ONLY)/storage-xxx.js ] + ! [ -e $(INVOCATION_ONLY)/SourceSerifPro-It.ttf.woff ] toolchain-only: $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx x.rs From 413938d7a9df4efe800552fa03ce1a5866539cbc Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 31 Mar 2021 17:23:32 -0400 Subject: [PATCH 5/5] Fix `--external-css` to be invocation-specific and note main.js should be invocation specific --- src/librustdoc/html/render/write_shared.rs | 11 ++++++++- src/test/run-make/emit-shared-files/Makefile | 24 ++++++++++++++------ src/test/run-make/emit-shared-files/y.css | 0 src/test/run-make/emit-shared-files/z.css | 0 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 src/test/run-make/emit-shared-files/y.css create mode 100644 src/test/run-make/emit-shared-files/z.css diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 34994097812c2..b2f2283c177df 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -213,6 +213,9 @@ pub(super) fn write_shared( let mut themes: Vec<&String> = themes.iter().collect(); themes.sort(); + // FIXME: this should probably not be a toolchain file since it depends on `--theme`. + // But it seems a shame to copy it over and over when it's almost always the same. + // Maybe we can change the representation to move this out of main.js? write_minify( "main.js", &static_files::MAIN_JS.replace( @@ -238,7 +241,13 @@ pub(super) fn write_shared( if let Some(ref css) = cx.shared.layout.css_file_extension { let buffer = try_err!(fs::read_to_string(css), css); - write_minify("theme.css", &buffer)?; + // This varies based on the invocation, so it can't go through the write_minify wrapper. + cx.write_minify( + SharedResource::InvocationSpecific { basename: "theme.css" }, + &buffer, + options.enable_minification, + &options.emit, + )?; } write_minify("normalize.css", static_files::NORMALIZE_CSS)?; for (name, contents) in &*FILES_UNVERSIONED { diff --git a/src/test/run-make/emit-shared-files/Makefile b/src/test/run-make/emit-shared-files/Makefile index 66a36dae9d370..5c4825ae66c87 100644 --- a/src/test/run-make/emit-shared-files/Makefile +++ b/src/test/run-make/emit-shared-files/Makefile @@ -7,30 +7,40 @@ ALL_SHARED = $(TMPDIR)/all-shared all: invocation-only toolchain-only all-shared invocation-only: - $(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css x.rs + $(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css --extend-css z.css x.rs [ -e $(INVOCATION_ONLY)/search-index-xxx.js ] [ -e $(INVOCATION_ONLY)/settings.html ] [ -e $(INVOCATION_ONLY)/x/all.html ] [ -e $(INVOCATION_ONLY)/x/index.html ] - # FIXME: this probably shouldn't have a suffix - [ -e $(INVOCATION_ONLY)/y-xxx.css ] + [ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css ! [ -e $(INVOCATION_ONLY)/storage-xxx.js ] ! [ -e $(INVOCATION_ONLY)/SourceSerifPro-It.ttf.woff ] + # FIXME: this probably shouldn't have a suffix + [ -e $(INVOCATION_ONLY)/y-xxx.css ] + # FIXME: this is technically incorrect (see `write_shared`) + ! [ -e $(INVOCATION_ONLY)/main-xxx.js ] + toolchain-only: - $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx x.rs + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs [ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ] - ! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ] ! [ -e $(TOOLCHAIN_ONLY)/SourceSerifPro-It.ttf.woff ] ! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ] ! [ -e $(TOOLCHAIN_ONLY)/x/index.html ] + ! [ -e $(TOOLCHAIN_ONLY)/theme.css ] + + [ -e $(TOOLCHAIN_ONLY)/main-xxx.js ] + ! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ] all-shared: - $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx x.rs + $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs [ -e $(ALL_SHARED)/storage-xxx.js ] [ -e $(ALL_SHARED)/SourceSerifPro-It.ttf.woff ] - ! [ -e $(ALL_SHARED)/y-xxx.css ] ! [ -e $(ALL_SHARED)/search-index-xxx.js ] ! [ -e $(ALL_SHARED)/settings.html ] ! [ -e $(ALL_SHARED)/x ] ! [ -e $(ALL_SHARED)/src ] + ! [ -e $(ALL_SHARED)/theme.css ] + + [ -e $(ALL_SHARED)/main-xxx.js ] + ! [ -e $(ALL_SHARED)/y-xxx.css ] diff --git a/src/test/run-make/emit-shared-files/y.css b/src/test/run-make/emit-shared-files/y.css new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/test/run-make/emit-shared-files/z.css b/src/test/run-make/emit-shared-files/z.css new file mode 100644 index 0000000000000..e69de29bb2d1d