Switch doc::{Std, Rustc} to crate_or_deps

Previously they were using `all_krates` and various hacks to determine
which crates to document. Switch them to `crate_or_deps` so `ShouldRun`
tells them which crate to document instead of having to guess.

This also makes a few other refactors:
- Remove the now unused `all_krates`; new code should only use
  `crate_or_deps`.
- Add tests for documenting Std
- Remove the unnecessary `run_cargo_rustdoc_for` closure so that we only
  run cargo once
- Give a more helpful error message when documenting a no_std target
- Use `builder.msg` in the Steps instead of `builder.info`
This commit is contained in:
jyn 2023-05-25 13:39:10 -05:00
parent cb4b7f6319
commit 58e18ddf86
5 changed files with 100 additions and 124 deletions

View File

@ -430,25 +430,6 @@ impl<'a> ShouldRun<'a> {
} }
} }
/// Indicates it should run if the command-line selects the given crate or
/// any of its (local) dependencies.
///
/// Compared to `krate`, this treats the dependencies as aliases for the
/// same job. Generally it is preferred to use `krate`, and treat each
/// individual path separately. For example `./x.py test src/liballoc`
/// (which uses `krate`) will test just `liballoc`. However, `./x.py check
/// src/liballoc` (which uses `all_krates`) will check all of `libtest`.
/// `all_krates` should probably be removed at some point.
pub fn all_krates(mut self, name: &str) -> Self {
let mut set = BTreeSet::new();
for krate in self.builder.in_tree_crates(name, None) {
let path = krate.local_path(self.builder);
set.insert(TaskPath { path, kind: Some(self.kind) });
}
self.paths.insert(PathSet::Set(set));
self
}
/// Indicates it should run if the command-line selects the given crate or /// Indicates it should run if the command-line selects the given crate or
/// any of its (local) dependencies. /// any of its (local) dependencies.
/// ///

View File

@ -1,5 +1,6 @@
use super::*; use super::*;
use crate::config::{Config, DryRun, TargetSelection}; use crate::config::{Config, DryRun, TargetSelection};
use crate::doc::DocumentationFormat;
use std::thread; use std::thread;
fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config { fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
@ -66,6 +67,16 @@ macro_rules! std {
}; };
} }
macro_rules! doc_std {
($host:ident => $target:ident, stage = $stage:literal) => {
doc::Std::new(
$stage,
TargetSelection::from_user(stringify!($target)),
DocumentationFormat::HTML,
)
};
}
macro_rules! rustc { macro_rules! rustc {
($host:ident => $target:ident, stage = $stage:literal) => { ($host:ident => $target:ident, stage = $stage:literal) => {
compile::Rustc::new( compile::Rustc::new(
@ -144,6 +155,9 @@ fn alias_and_path_for_library() {
first(cache.all::<compile::Std>()), first(cache.all::<compile::Std>()),
&[std!(A => A, stage = 0), std!(A => A, stage = 1)] &[std!(A => A, stage = 0), std!(A => A, stage = 1)]
); );
let mut cache = run_build(&["library".into(), "core".into()], configure("doc", &["A"], &["A"]));
assert_eq!(first(cache.all::<doc::Std>()), &[doc_std!(A => A, stage = 0)]);
} }
#[test] #[test]

View File

@ -106,11 +106,7 @@ impl Step for JsonDocs {
/// Builds the `rust-docs-json` installer component. /// Builds the `rust-docs-json` installer component.
fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> { fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
let host = self.host; let host = self.host;
builder.ensure(crate::doc::Std { builder.ensure(crate::doc::Std::new(builder.top_stage, host, DocumentationFormat::JSON));
stage: builder.top_stage,
target: host,
format: DocumentationFormat::JSON,
});
let dest = "share/doc/rust/json"; let dest = "share/doc/rust/json";

View File

@ -16,6 +16,7 @@ use crate::builder::crate_description;
use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step};
use crate::cache::{Interned, INTERNER}; use crate::cache::{Interned, INTERNER};
use crate::compile; use crate::compile;
use crate::compile::make_run_crates;
use crate::config::{Config, TargetSelection}; use crate::config::{Config, TargetSelection};
use crate::tool::{self, prepare_tool_cargo, SourceType, Tool}; use crate::tool::{self, prepare_tool_cargo, SourceType, Tool};
use crate::util::{symlink_dir, t, up_to_date}; use crate::util::{symlink_dir, t, up_to_date};
@ -87,15 +88,6 @@ book!(
StyleGuide, "src/doc/style-guide", "style-guide"; StyleGuide, "src/doc/style-guide", "style-guide";
); );
// "library/std" -> ["library", "std"]
//
// Used for deciding whether a particular step is one requested by the user on
// the `x.py doc` command line, which determines whether `--open` will open that
// page.
pub(crate) fn components_simplified(path: &PathBuf) -> Vec<&str> {
path.iter().map(|component| component.to_str().unwrap_or("???")).collect()
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct UnstableBook { pub struct UnstableBook {
target: TargetSelection, target: TargetSelection,
@ -425,11 +417,18 @@ impl Step for SharedAssets {
} }
} }
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Std { pub struct Std {
pub stage: u32, pub stage: u32,
pub target: TargetSelection, pub target: TargetSelection,
pub format: DocumentationFormat, pub format: DocumentationFormat,
crates: Interned<Vec<String>>,
}
impl Std {
pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self {
Std { stage, target, format, crates: INTERNER.intern_list(vec![]) }
}
} }
impl Step for Std { impl Step for Std {
@ -438,7 +437,7 @@ impl Step for Std {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder; let builder = run.builder;
run.all_krates("sysroot").path("library").default_condition(builder.config.docs) run.crate_or_deps("sysroot").path("library").default_condition(builder.config.docs)
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {
@ -450,6 +449,7 @@ impl Step for Std {
} else { } else {
DocumentationFormat::HTML DocumentationFormat::HTML
}, },
crates: make_run_crates(&run, "library"),
}); });
} }
@ -457,7 +457,7 @@ impl Step for Std {
/// ///
/// This will generate all documentation for the standard library and its /// This will generate all documentation for the standard library and its
/// dependencies. This is largely just a wrapper around `cargo doc`. /// dependencies. This is largely just a wrapper around `cargo doc`.
fn run(self, builder: &Builder<'_>) { fn run(mut self, builder: &Builder<'_>) {
let stage = self.stage; let stage = self.stage;
let target = self.target; let target = self.target;
let out = match self.format { let out = match self.format {
@ -487,25 +487,7 @@ impl Step for Std {
extra_args.push(OsStr::new("--disable-minification")); extra_args.push(OsStr::new("--disable-minification"));
} }
let requested_crates = builder doc_std(builder, self.format, stage, target, &out, &extra_args, &self.crates);
.paths
.iter()
.map(components_simplified)
.filter_map(|path| {
if path.len() >= 2 && path.get(0) == Some(&"library") {
// single crate
Some(path[1].to_owned())
} else if !path.is_empty() {
// ??
Some(path[0].to_owned())
} else {
// all library crates
None
}
})
.collect::<Vec<_>>();
doc_std(builder, self.format, stage, target, &out, &extra_args, &requested_crates);
// Don't open if the format is json // Don't open if the format is json
if let DocumentationFormat::JSON = self.format { if let DocumentationFormat::JSON = self.format {
@ -514,7 +496,11 @@ impl Step for Std {
// Look for library/std, library/core etc in the `x.py doc` arguments and // Look for library/std, library/core etc in the `x.py doc` arguments and
// open the corresponding rendered docs. // open the corresponding rendered docs.
for requested_crate in requested_crates { if self.crates.is_empty() {
self.crates = INTERNER.intern_list(vec!["library".to_owned()]);
};
for requested_crate in &*self.crates {
if requested_crate == "library" { if requested_crate == "library" {
// For `x.py doc library --open`, open `std` by default. // For `x.py doc library --open`, open `std` by default.
let index = out.join("std").join("index.html"); let index = out.join("std").join("index.html");
@ -538,7 +524,7 @@ impl Step for Std {
/// or remote link. /// or remote link.
const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"]; const STD_PUBLIC_CRATES: [&str; 5] = ["core", "alloc", "std", "proc_macro", "test"];
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum DocumentationFormat { pub enum DocumentationFormat {
HTML, HTML,
JSON, JSON,
@ -566,21 +552,19 @@ fn doc_std(
extra_args: &[&OsStr], extra_args: &[&OsStr],
requested_crates: &[String], requested_crates: &[String],
) { ) {
builder.info(&format!(
"Documenting{} stage{} library ({}) in {} format",
crate_description(requested_crates),
stage,
target,
format.as_str()
));
if builder.no_std(target) == Some(true) { if builder.no_std(target) == Some(true) {
panic!( panic!(
"building std documentation for no_std target {target} is not supported\n\ "building std documentation for no_std target {target} is not supported\n\
Set `docs = false` in the config to disable documentation." Set `docs = false` in the config to disable documentation, or pass `--exclude doc::library`."
); );
} }
let compiler = builder.compiler(stage, builder.config.build); let compiler = builder.compiler(stage, builder.config.build);
let description =
format!("library{} in {} format", crate_description(&requested_crates), format.as_str());
let _guard = builder.msg(Kind::Doc, stage, &description, compiler.host, target);
let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" }; let target_doc_dir_name = if format == DocumentationFormat::JSON { "json-doc" } else { "doc" };
let target_dir = let target_dir =
builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name); builder.stage_out(compiler, Mode::Std).join(target.triple).join(target_doc_dir_name);
@ -590,35 +574,27 @@ fn doc_std(
// as a function parameter. // as a function parameter.
let out_dir = target_dir.join(target.triple).join("doc"); let out_dir = target_dir.join(target.triple).join("doc");
let run_cargo_rustdoc_for = |package: &str| {
let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "rustdoc"); let mut cargo = builder.cargo(compiler, Mode::Std, SourceType::InTree, target, "rustdoc");
compile::std_cargo(builder, target, compiler.stage, &mut cargo); compile::std_cargo(builder, target, compiler.stage, &mut cargo);
cargo.arg("--target-dir").arg(&*target_dir.to_string_lossy()).arg("-Zskip-rustdoc-fingerprint");
for krate in requested_crates {
cargo.arg("-p").arg(krate);
}
cargo cargo
.arg("--target-dir")
.arg(&*target_dir.to_string_lossy())
.arg("-p")
.arg(package)
.arg("-Zskip-rustdoc-fingerprint")
.arg("--") .arg("--")
.arg("-Z") .arg("-Z")
.arg("unstable-options") .arg("unstable-options")
.arg("--resource-suffix") .arg("--resource-suffix")
.arg(&builder.version) .arg(&builder.version)
.args(extra_args); .args(extra_args);
if builder.config.library_docs_private_items { if builder.config.library_docs_private_items {
cargo.arg("--document-private-items").arg("--document-hidden-items"); cargo.arg("--document-private-items").arg("--document-hidden-items");
} }
builder.run(&mut cargo.into()); builder.run(&mut cargo.into());
};
for krate in STD_PUBLIC_CRATES {
run_cargo_rustdoc_for(krate);
if requested_crates.iter().any(|p| p == krate) {
// No need to document more of the libraries if we have the one we want.
break;
}
}
builder.cp_r(&out_dir, &out); builder.cp_r(&out_dir, &out);
} }
@ -626,6 +602,28 @@ fn doc_std(
pub struct Rustc { pub struct Rustc {
pub stage: u32, pub stage: u32,
pub target: TargetSelection, pub target: TargetSelection,
crates: Interned<Vec<String>>,
}
impl Rustc {
pub(crate) fn new(stage: u32, target: TargetSelection, builder: &Builder<'_>) -> Self {
// Find dependencies for top level crates.
let root_crates = vec![
INTERNER.intern_str("rustc_driver"),
INTERNER.intern_str("rustc_codegen_llvm"),
INTERNER.intern_str("rustc_codegen_ssa"),
];
let crates: Vec<_> = root_crates
.iter()
.flat_map(|krate| {
builder
.in_tree_crates(krate, Some(target))
.into_iter()
.map(|krate| krate.name.to_string())
})
.collect();
Self { stage, target, crates: INTERNER.intern_list(crates) }
}
} }
impl Step for Rustc { impl Step for Rustc {
@ -641,7 +639,11 @@ impl Step for Rustc {
} }
fn make_run(run: RunConfig<'_>) { fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustc { stage: run.builder.top_stage, target: run.target }); run.builder.ensure(Rustc {
stage: run.builder.top_stage,
target: run.target,
crates: make_run_crates(&run, "compiler"),
});
} }
/// Generates compiler documentation. /// Generates compiler documentation.
@ -654,15 +656,6 @@ impl Step for Rustc {
let stage = self.stage; let stage = self.stage;
let target = self.target; let target = self.target;
let paths = builder
.paths
.iter()
.filter(|path| {
let components = components_simplified(path);
components.len() >= 2 && components[0] == "compiler"
})
.collect::<Vec<_>>();
// This is the intended out directory for compiler documentation. // This is the intended out directory for compiler documentation.
let out = builder.compiler_doc_out(target); let out = builder.compiler_doc_out(target);
t!(fs::create_dir_all(&out)); t!(fs::create_dir_all(&out));
@ -672,7 +665,13 @@ impl Step for Rustc {
let compiler = builder.compiler(stage, builder.config.build); let compiler = builder.compiler(stage, builder.config.build);
builder.ensure(compile::Std::new(compiler, builder.config.build)); builder.ensure(compile::Std::new(compiler, builder.config.build));
builder.info(&format!("Documenting stage{} compiler ({})", stage, target)); let _guard = builder.msg(
Kind::Doc,
stage,
&format!("compiler{}", crate_description(&self.crates)),
compiler.host,
target,
);
// This uses a shared directory so that librustdoc documentation gets // This uses a shared directory so that librustdoc documentation gets
// correctly built and merged with the rustc documentation. This is // correctly built and merged with the rustc documentation. This is
@ -710,22 +709,8 @@ impl Step for Rustc {
cargo.rustdocflag("--extern-html-root-url"); cargo.rustdocflag("--extern-html-root-url");
cargo.rustdocflag("ena=https://docs.rs/ena/latest/"); cargo.rustdocflag("ena=https://docs.rs/ena/latest/");
let root_crates = if paths.is_empty() {
vec![
INTERNER.intern_str("rustc_driver"),
INTERNER.intern_str("rustc_codegen_llvm"),
INTERNER.intern_str("rustc_codegen_ssa"),
]
} else {
paths.into_iter().map(|p| builder.crate_paths[p]).collect()
};
// Find dependencies for top level crates.
let compiler_crates = root_crates.iter().flat_map(|krate| {
builder.in_tree_crates(krate, Some(target)).into_iter().map(|krate| krate.name)
});
let mut to_open = None; let mut to_open = None;
for krate in compiler_crates { for krate in &*self.crates {
// Create all crate output directories first to make sure rustdoc uses // Create all crate output directories first to make sure rustdoc uses
// relative links. // relative links.
// FIXME: Cargo should probably do this itself. // FIXME: Cargo should probably do this itself.
@ -785,7 +770,7 @@ macro_rules! tool_doc {
if true $(&& $rustc_tool)? { if true $(&& $rustc_tool)? {
// Build rustc docs so that we generate relative links. // Build rustc docs so that we generate relative links.
builder.ensure(Rustc { stage, target }); builder.ensure(Rustc::new(stage, target, builder));
// Rustdoc needs the rustc sysroot available to build. // Rustdoc needs the rustc sysroot available to build.
// FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed

View File

@ -220,7 +220,7 @@ impl Step for HtmlCheck {
} }
// Ensure that a few different kinds of documentation are available. // Ensure that a few different kinds of documentation are available.
builder.default_doc(&[]); builder.default_doc(&[]);
builder.ensure(crate::doc::Rustc { target: self.target, stage: builder.top_stage }); builder.ensure(crate::doc::Rustc::new(builder.top_stage, self.target, builder));
try_run(builder, builder.tool_cmd(Tool::HtmlChecker).arg(builder.doc_out(self.target))); try_run(builder, builder.tool_cmd(Tool::HtmlChecker).arg(builder.doc_out(self.target)));
} }
@ -886,11 +886,11 @@ impl Step for RustdocJSStd {
command.arg("--test-file").arg(path); command.arg("--test-file").arg(path);
} }
} }
builder.ensure(crate::doc::Std { builder.ensure(crate::doc::Std::new(
target: self.target, builder.top_stage,
stage: builder.top_stage, self.target,
format: DocumentationFormat::HTML, DocumentationFormat::HTML,
}); ));
builder.run(&mut command); builder.run(&mut command);
} else { } else {
builder.info("No nodejs found, skipping \"tests/rustdoc-js-std\" tests"); builder.info("No nodejs found, skipping \"tests/rustdoc-js-std\" tests");