Skip to content

Commit 2e1cba6

Browse files
EtomicBombethan
authored and
ethan
committed
rfc#3662 changes under unstable flags
* All new functionality is under unstable options * Adds `--merge=shared|none|finalize` flags * Adds `--parts-out-dir=<crate specific directory>` for `--merge=none` to write cross-crate info file for a single crate * Adds `--include-parts-dir=<previously specified directory>` for `--merge=finalize` to write cross-crate info files * update tests/run-make/rustdoc-default-output/rmake.rs golden
1 parent 3782251 commit 2e1cba6

File tree

7 files changed

+419
-135
lines changed

7 files changed

+419
-135
lines changed

Diff for: src/librustdoc/config.rs

+105-4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ impl TryFrom<&str> for OutputFormat {
5353
}
5454
}
5555

56+
/// Either an input crate, markdown file, or nothing (--merge=finalize).
57+
pub(crate) enum InputMode {
58+
/// The `--merge=finalize` step does not need an input crate to rustdoc.
59+
NoInputMergeFinalize,
60+
/// A crate or markdown file.
61+
HasFile(Input),
62+
}
63+
5664
/// Configuration options for rustdoc.
5765
#[derive(Clone)]
5866
pub(crate) struct Options {
@@ -286,6 +294,12 @@ pub(crate) struct RenderOptions {
286294
/// This field is only used for the JSON output. If it's set to true, no file will be created
287295
/// and content will be displayed in stdout directly.
288296
pub(crate) output_to_stdout: bool,
297+
/// Whether we should read or write rendered cross-crate info in the doc root.
298+
pub(crate) should_merge: ShouldMerge,
299+
/// Path to crate-info for external crates.
300+
pub(crate) include_parts_dir: Vec<PathToParts>,
301+
/// Where to write crate-info
302+
pub(crate) parts_out_dir: Option<PathToParts>,
289303
}
290304

291305
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -345,7 +359,7 @@ impl Options {
345359
early_dcx: &mut EarlyDiagCtxt,
346360
matches: &getopts::Matches,
347361
args: Vec<String>,
348-
) -> Option<(Input, Options, RenderOptions)> {
362+
) -> Option<(InputMode, Options, RenderOptions)> {
349363
// Check for unstable options.
350364
nightly_options::check_nightly_options(early_dcx, matches, &opts());
351365

@@ -475,22 +489,34 @@ impl Options {
475489
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
476490

477491
let input = if describe_lints {
478-
"" // dummy, this won't be used
492+
InputMode::HasFile(make_input(early_dcx, ""))
479493
} else {
480494
match matches.free.as_slice() {
495+
[] if matches.opt_str("merge").as_deref() == Some("finalize") => {
496+
InputMode::NoInputMergeFinalize
497+
}
481498
[] => dcx.fatal("missing file operand"),
482-
[input] => input,
499+
[input] => InputMode::HasFile(make_input(early_dcx, input)),
483500
_ => dcx.fatal("too many file operands"),
484501
}
485502
};
486-
let input = make_input(early_dcx, input);
487503

488504
let externs = parse_externs(early_dcx, matches, &unstable_opts);
489505
let extern_html_root_urls = match parse_extern_html_roots(matches) {
490506
Ok(ex) => ex,
491507
Err(err) => dcx.fatal(err),
492508
};
493509

510+
let parts_out_dir =
511+
match matches.opt_str("parts-out-dir").map(|p| PathToParts::from_flag(p)).transpose() {
512+
Ok(parts_out_dir) => parts_out_dir,
513+
Err(e) => dcx.fatal(e),
514+
};
515+
let include_parts_dir = match parse_include_parts_dir(matches) {
516+
Ok(include_parts_dir) => include_parts_dir,
517+
Err(e) => dcx.fatal(e),
518+
};
519+
494520
let default_settings: Vec<Vec<(String, String)>> = vec![
495521
matches
496522
.opt_str("default-theme")
@@ -732,6 +758,10 @@ impl Options {
732758
let extern_html_root_takes_precedence =
733759
matches.opt_present("extern-html-root-takes-precedence");
734760
let html_no_source = matches.opt_present("html-no-source");
761+
let should_merge = match parse_merge(matches) {
762+
Ok(result) => result,
763+
Err(e) => dcx.fatal(format!("--merge option error: {e}")),
764+
};
735765

736766
if generate_link_to_definition && (show_coverage || output_format != OutputFormat::Html) {
737767
dcx.struct_warn(
@@ -819,6 +849,9 @@ impl Options {
819849
no_emit_shared: false,
820850
html_no_source,
821851
output_to_stdout,
852+
should_merge,
853+
include_parts_dir,
854+
parts_out_dir,
822855
};
823856
Some((input, options, render_options))
824857
}
@@ -894,3 +927,71 @@ fn parse_extern_html_roots(
894927
}
895928
Ok(externs)
896929
}
930+
931+
/// Path directly to crate-info file.
932+
///
933+
/// For example, `/home/user/project/target/doc.parts/<crate>/crate-info`.
934+
#[derive(Clone, Debug)]
935+
pub(crate) struct PathToParts(pub(crate) PathBuf);
936+
937+
impl PathToParts {
938+
fn from_flag(path: String) -> Result<PathToParts, String> {
939+
let mut path = PathBuf::from(path);
940+
// check here is for diagnostics
941+
if path.exists() && !path.is_dir() {
942+
Err(format!(
943+
"--parts-out-dir and --include-parts-dir expect directories, found: {}",
944+
path.display(),
945+
))
946+
} else {
947+
// if it doesn't exist, we'll create it. worry about that in write_shared
948+
path.push("crate-info");
949+
Ok(PathToParts(path))
950+
}
951+
}
952+
}
953+
954+
/// Reports error if --include-parts-dir / crate-info is not a file
955+
fn parse_include_parts_dir(m: &getopts::Matches) -> Result<Vec<PathToParts>, String> {
956+
let mut ret = Vec::new();
957+
for p in m.opt_strs("include-parts-dir") {
958+
let p = PathToParts::from_flag(p)?;
959+
// this is just for diagnostic
960+
if !p.0.is_file() {
961+
return Err(format!("--include-parts-dir expected {} to be a file", p.0.display()));
962+
}
963+
ret.push(p);
964+
}
965+
Ok(ret)
966+
}
967+
968+
/// Controls merging of cross-crate information
969+
#[derive(Debug, Clone)]
970+
pub(crate) struct ShouldMerge {
971+
/// Should we append to existing cci in the doc root
972+
pub(crate) read_rendered_cci: bool,
973+
/// Should we write cci to the doc root
974+
pub(crate) write_rendered_cci: bool,
975+
}
976+
977+
/// Extracts read_rendered_cci and write_rendered_cci from command line arguments, or
978+
/// reports an error if an invalid option was provided
979+
fn parse_merge(m: &getopts::Matches) -> Result<ShouldMerge, &'static str> {
980+
match m.opt_str("merge").as_deref() {
981+
// default = read-write
982+
None => Ok(ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }),
983+
Some("none") if m.opt_present("include-parts-dir") => {
984+
Err("--include-parts-dir not allowed if --merge=none")
985+
}
986+
Some("none") => Ok(ShouldMerge { read_rendered_cci: false, write_rendered_cci: false }),
987+
Some("shared") if m.opt_present("parts-out-dir") || m.opt_present("include-parts-dir") => {
988+
Err("--parts-out-dir and --include-parts-dir not allowed if --merge=shared")
989+
}
990+
Some("shared") => Ok(ShouldMerge { read_rendered_cci: true, write_rendered_cci: true }),
991+
Some("finalize") if m.opt_present("parts-out-dir") => {
992+
Err("--parts-out-dir not allowed if --merge=finalize")
993+
}
994+
Some("finalize") => Ok(ShouldMerge { read_rendered_cci: false, write_rendered_cci: true }),
995+
Some(_) => Err("argument to --merge must be `none`, `shared`, or `finalize`"),
996+
}
997+
}

Diff for: src/librustdoc/html/render/context.rs

+91-84
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@ use tracing::info;
1616

1717
use super::print_item::{full_path, item_path, print_item};
1818
use super::sidebar::{print_sidebar, sidebar_module_like, ModuleLike, Sidebar};
19-
use super::write_shared::write_shared;
2019
use super::{collect_spans_and_sources, scrape_examples_help, AllTypes, LinkFromSrc, StylePath};
2120
use crate::clean::types::ExternalLocation;
2221
use crate::clean::utils::has_doc_flag;
2322
use crate::clean::{self, ExternalCrate};
24-
use crate::config::{ModuleSorting, RenderOptions};
23+
use crate::config::{ModuleSorting, RenderOptions, ShouldMerge};
2524
use crate::docfs::{DocFS, PathError};
2625
use crate::error::Error;
2726
use crate::formats::cache::Cache;
@@ -30,6 +29,7 @@ use crate::formats::FormatRenderer;
3029
use crate::html::escape::Escape;
3130
use crate::html::format::{join_with_double_colon, Buffer};
3231
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
32+
use crate::html::render::write_shared::write_shared;
3333
use crate::html::url_parts_builder::UrlPartsBuilder;
3434
use crate::html::{layout, sources, static_files};
3535
use crate::scrape_examples::AllCallLocations;
@@ -128,8 +128,10 @@ pub(crate) struct SharedContext<'tcx> {
128128
pub(crate) span_correspondence_map: FxHashMap<rustc_span::Span, LinkFromSrc>,
129129
/// The [`Cache`] used during rendering.
130130
pub(crate) cache: Cache,
131-
132131
pub(crate) call_locations: AllCallLocations,
132+
/// Controls whether we read / write to cci files in the doc root. Defaults read=true,
133+
/// write=true
134+
should_merge: ShouldMerge,
133135
}
134136

135137
impl SharedContext<'_> {
@@ -551,6 +553,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
551553
span_correspondence_map: matches,
552554
cache,
553555
call_locations,
556+
should_merge: options.should_merge,
554557
};
555558

556559
let dst = output;
@@ -640,92 +643,96 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
640643
);
641644
shared.fs.write(final_file, v)?;
642645

643-
// Generating settings page.
644-
page.title = "Settings";
645-
page.description = "Settings of Rustdoc";
646-
page.root_path = "./";
647-
page.rust_logo = true;
646+
// if to avoid writing help, settings files to doc root unless we're on the final invocation
647+
if shared.should_merge.write_rendered_cci {
648+
// Generating settings page.
649+
page.title = "Settings";
650+
page.description = "Settings of Rustdoc";
651+
page.root_path = "./";
652+
page.rust_logo = true;
648653

649-
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
650-
let v = layout::render(
651-
&shared.layout,
652-
&page,
653-
sidebar,
654-
|buf: &mut Buffer| {
655-
write!(
656-
buf,
657-
"<div class=\"main-heading\">\
658-
<h1>Rustdoc settings</h1>\
659-
<span class=\"out-of-band\">\
660-
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
661-
Back\
662-
</a>\
663-
</span>\
664-
</div>\
665-
<noscript>\
666-
<section>\
667-
You need to enable JavaScript be able to update your settings.\
668-
</section>\
669-
</noscript>\
670-
<script defer src=\"{static_root_path}{settings_js}\"></script>",
671-
static_root_path = page.get_static_root_path(),
672-
settings_js = static_files::STATIC_FILES.settings_js,
673-
);
674-
// Pre-load all theme CSS files, so that switching feels seamless.
675-
//
676-
// When loading settings.html as a popover, the equivalent HTML is
677-
// generated in main.js.
678-
for file in &shared.style_files {
679-
if let Ok(theme) = file.basename() {
680-
write!(
681-
buf,
682-
"<link rel=\"preload\" href=\"{root_path}{theme}{suffix}.css\" \
683-
as=\"style\">",
684-
root_path = page.static_root_path.unwrap_or(""),
685-
suffix = page.resource_suffix,
686-
);
654+
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
655+
let v = layout::render(
656+
&shared.layout,
657+
&page,
658+
sidebar,
659+
|buf: &mut Buffer| {
660+
write!(
661+
buf,
662+
"<div class=\"main-heading\">\
663+
<h1>Rustdoc settings</h1>\
664+
<span class=\"out-of-band\">\
665+
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
666+
Back\
667+
</a>\
668+
</span>\
669+
</div>\
670+
<noscript>\
671+
<section>\
672+
You need to enable JavaScript be able to update your settings.\
673+
</section>\
674+
</noscript>\
675+
<script defer src=\"{static_root_path}{settings_js}\"></script>",
676+
static_root_path = page.get_static_root_path(),
677+
settings_js = static_files::STATIC_FILES.settings_js,
678+
);
679+
// Pre-load all theme CSS files, so that switching feels seamless.
680+
//
681+
// When loading settings.html as a popover, the equivalent HTML is
682+
// generated in main.js.
683+
for file in &shared.style_files {
684+
if let Ok(theme) = file.basename() {
685+
write!(
686+
buf,
687+
"<link rel=\"preload\" href=\"{root_path}{theme}{suffix}.css\" \
688+
as=\"style\">",
689+
root_path = page.static_root_path.unwrap_or(""),
690+
suffix = page.resource_suffix,
691+
);
692+
}
687693
}
688-
}
689-
},
690-
&shared.style_files,
691-
);
692-
shared.fs.write(settings_file, v)?;
694+
},
695+
&shared.style_files,
696+
);
697+
shared.fs.write(settings_file, v)?;
693698

694-
// Generating help page.
695-
page.title = "Help";
696-
page.description = "Documentation for Rustdoc";
697-
page.root_path = "./";
698-
page.rust_logo = true;
699+
// Generating help page.
700+
page.title = "Help";
701+
page.description = "Documentation for Rustdoc";
702+
page.root_path = "./";
703+
page.rust_logo = true;
699704

700-
let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
701-
let v = layout::render(
702-
&shared.layout,
703-
&page,
704-
sidebar,
705-
|buf: &mut Buffer| {
706-
write!(
707-
buf,
708-
"<div class=\"main-heading\">\
709-
<h1>Rustdoc help</h1>\
710-
<span class=\"out-of-band\">\
711-
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
712-
Back\
713-
</a>\
714-
</span>\
715-
</div>\
716-
<noscript>\
717-
<section>\
718-
<p>You need to enable JavaScript to use keyboard commands or search.</p>\
719-
<p>For more information, browse the <a href=\"https://doc.rust-lang.org/rustdoc/\">rustdoc handbook</a>.</p>\
720-
</section>\
721-
</noscript>",
722-
)
723-
},
724-
&shared.style_files,
725-
);
726-
shared.fs.write(help_file, v)?;
705+
let sidebar = "<h2 class=\"location\">Help</h2><div class=\"sidebar-elems\"></div>";
706+
let v = layout::render(
707+
&shared.layout,
708+
&page,
709+
sidebar,
710+
|buf: &mut Buffer| {
711+
write!(
712+
buf,
713+
"<div class=\"main-heading\">\
714+
<h1>Rustdoc help</h1>\
715+
<span class=\"out-of-band\">\
716+
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
717+
Back\
718+
</a>\
719+
</span>\
720+
</div>\
721+
<noscript>\
722+
<section>\
723+
<p>You need to enable JavaScript to use keyboard commands or search.</p>\
724+
<p>For more information, browse the <a href=\"https://doc.rust-lang.org/rustdoc/\">rustdoc handbook</a>.</p>\
725+
</section>\
726+
</noscript>",
727+
)
728+
},
729+
&shared.style_files,
730+
);
731+
shared.fs.write(help_file, v)?;
732+
}
727733

728-
if shared.layout.scrape_examples_extension {
734+
// if to avoid writing files to doc root unless we're on the final invocation
735+
if shared.layout.scrape_examples_extension && shared.should_merge.write_rendered_cci {
729736
page.title = "About scraped examples";
730737
page.description = "How the scraped examples feature works in Rustdoc";
731738
let v = layout::render(

Diff for: src/librustdoc/html/render/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use tracing::{debug, info};
6161

6262
pub(crate) use self::context::*;
6363
pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc};
64+
pub(crate) use self::write_shared::*;
6465
use crate::clean::{self, ItemId, RenderedLink};
6566
use crate::error::Error;
6667
use crate::formats::cache::Cache;

0 commit comments

Comments
 (0)