Skip to content

Commit 469da4c

Browse files
committed
rfc#3662 changes under unstable flags
* All new functionality is under unstable options * Missing (will add later) functionality for --merge=finalize without a crate root * 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 5aea140 commit 469da4c

File tree

6 files changed

+309
-113
lines changed

6 files changed

+309
-113
lines changed

Diff for: src/librustdoc/config.rs

+90
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ pub(crate) struct RenderOptions {
289289
/// This field is only used for the JSON output. If it's set to true, no file will be created
290290
/// and content will be displayed in stdout directly.
291291
pub(crate) output_to_stdout: bool,
292+
/// Whether we should read or write rendered cross-crate info in the doc root.
293+
pub(crate) should_merge: ShouldMerge,
294+
/// Path to crate-info for external crates.
295+
pub(crate) include_parts_dir: Vec<PathToParts>,
296+
/// Where to write crate-info
297+
pub(crate) parts_out_dir: Option<PathToParts>,
292298
}
293299

294300
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -494,6 +500,16 @@ impl Options {
494500
Err(err) => dcx.fatal(err),
495501
};
496502

503+
let parts_out_dir =
504+
match matches.opt_str("parts-out-dir").map(|p| PathToParts::from_flag(p)).transpose() {
505+
Ok(parts_out_dir) => parts_out_dir,
506+
Err(e) => dcx.fatal(e),
507+
};
508+
let include_parts_dir = match parse_include_parts_dir(matches) {
509+
Ok(include_parts_dir) => include_parts_dir,
510+
Err(e) => dcx.fatal(e),
511+
};
512+
497513
let default_settings: Vec<Vec<(String, String)>> = vec![
498514
matches
499515
.opt_str("default-theme")
@@ -735,6 +751,10 @@ impl Options {
735751
let extern_html_root_takes_precedence =
736752
matches.opt_present("extern-html-root-takes-precedence");
737753
let html_no_source = matches.opt_present("html-no-source");
754+
let should_merge = match parse_merge(matches) {
755+
Ok(result) => result,
756+
Err(e) => dcx.fatal(format!("--merge option error: {e}")),
757+
};
738758

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

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

+90-83
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use super::{collect_spans_and_sources, scrape_examples_help, AllTypes, LinkFromS
2020
use crate::clean::types::ExternalLocation;
2121
use crate::clean::utils::has_doc_flag;
2222
use crate::clean::{self, ExternalCrate};
23-
use crate::config::{ModuleSorting, RenderOptions};
23+
use crate::config::{ModuleSorting, RenderOptions, ShouldMerge};
2424
use crate::docfs::{DocFS, PathError};
2525
use crate::error::Error;
2626
use crate::formats::cache::Cache;
@@ -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(

0 commit comments

Comments
 (0)