Skip to content

Commit dc67e7c

Browse files
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 8343407 commit dc67e7c

File tree

7 files changed

+419
-135
lines changed

7 files changed

+419
-135
lines changed

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+
}

src/librustdoc/html/render/context.rs

+91-84
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ use rustc_span::{sym, FileName, Symbol};
1515

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

134136
impl SharedContext<'_> {
@@ -550,6 +552,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
550552
span_correspondence_map: matches,
551553
cache,
552554
call_locations,
555+
should_merge: options.should_merge,
553556
};
554557

555558
let dst = output;
@@ -637,92 +640,96 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
637640
);
638641
shared.fs.write(final_file, v)?;
639642

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

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

691-
// Generating help page.
692-
page.title = "Help";
693-
page.description = "Documentation for Rustdoc";
694-
page.root_path = "./";
695-
page.rust_logo = true;
696+
// Generating help page.
697+
page.title = "Help";
698+
page.description = "Documentation for Rustdoc";
699+
page.root_path = "./";
700+
page.rust_logo = true;
696701

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

725-
if shared.layout.scrape_examples_extension {
731+
// if to avoid writing files to doc root unless we're on the final invocation
732+
if shared.layout.scrape_examples_extension && shared.should_merge.write_rendered_cci {
726733
page.title = "About scraped examples";
727734
page.description = "How the scraped examples feature works in Rustdoc";
728735
let v = layout::render(

src/librustdoc/html/render/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use serde::{Serialize, Serializer};
6060

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

0 commit comments

Comments
 (0)