diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index c0c749552e6..535a005c396 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -115,6 +115,43 @@ pub fn cargo_crates_in_set(&self) -> Interned> { } INTERNER.intern_list(crates) } + + /// Given an `alias` selected by the `Step` and the paths passed on the command line, + /// return a list of the crates that should be built. + /// + /// Normally, people will pass *just* `library` if they pass it. + /// But it's possible (although strange) to pass something like `library std core`. + /// Build all crates anyway, as if they hadn't passed the other args. + pub fn make_run_crates(&self, alias: Alias) -> Interned> { + let has_alias = + self.paths.iter().any(|set| set.assert_single_path().path.ends_with(alias.as_str())); + if !has_alias { + return self.cargo_crates_in_set(); + } + + let crates = match alias { + Alias::Library => self.builder.in_tree_crates("sysroot", Some(self.target)), + Alias::Compiler => self.builder.in_tree_crates("rustc-main", Some(self.target)), + }; + + let crate_names = crates.into_iter().map(|krate| krate.name.to_string()).collect(); + INTERNER.intern_list(crate_names) + } +} + +#[derive(Debug, Copy, Clone)] +pub enum Alias { + Library, + Compiler, +} + +impl Alias { + fn as_str(self) -> &'static str { + match self { + Alias::Library => "library", + Alias::Compiler => "compiler", + } + } } /// A description of the crates in this set, suitable for passing to `builder.info`. diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs index 31dcee58216..65b8f7fd3b7 100644 --- a/src/bootstrap/builder/tests.rs +++ b/src/bootstrap/builder/tests.rs @@ -68,13 +68,17 @@ macro_rules! std { } macro_rules! doc_std { - ($host:ident => $target:ident, stage = $stage:literal) => { + ($host:ident => $target:ident, stage = $stage:literal) => {{ + let config = configure("doc", &["A"], &["A"]); + let build = Build::new(config); + let builder = Builder::new(&build); doc::Std::new( $stage, TargetSelection::from_user(stringify!($target)), + &builder, DocumentationFormat::HTML, ) - }; + }}; } macro_rules! rustc { diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 1a0f0047812..691e5ce4eb2 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -1,10 +1,8 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. -use crate::builder::{crate_description, Builder, Kind, RunConfig, ShouldRun, Step}; +use crate::builder::{crate_description, Alias, Builder, Kind, RunConfig, ShouldRun, Step}; use crate::cache::Interned; -use crate::compile::{ - add_to_sysroot, make_run_crates, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, -}; +use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo}; use crate::config::TargetSelection; use crate::tool::{prepare_tool_cargo, SourceType}; use crate::INTERNER; @@ -89,7 +87,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { } fn make_run(run: RunConfig<'_>) { - let crates = make_run_crates(&run, "library"); + let crates = run.make_run_crates(Alias::Library); run.builder.ensure(Std { target: run.target, crates }); } @@ -140,7 +138,7 @@ fn run(self, builder: &Builder<'_>) { // don't run on std twice with x.py clippy // don't check test dependencies if we haven't built libtest - if builder.kind == Kind::Clippy || !self.crates.is_empty() { + if builder.kind == Kind::Clippy || !self.crates.iter().any(|krate| krate == "test") { return; } @@ -200,10 +198,11 @@ pub struct Rustc { impl Rustc { pub fn new(target: TargetSelection, builder: &Builder<'_>) -> Self { - let mut crates = vec![]; - for krate in builder.in_tree_crates("rustc-main", None) { - crates.push(krate.name.to_string()); - } + let crates = builder + .in_tree_crates("rustc-main", Some(target)) + .into_iter() + .map(|krate| krate.name.to_string()) + .collect(); Self { target, crates: INTERNER.intern_list(crates) } } } @@ -218,7 +217,7 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { } fn make_run(run: RunConfig<'_>) { - let crates = make_run_crates(&run, "compiler"); + let crates = run.make_run_crates(Alias::Compiler); run.builder.ensure(Rustc { target: run.target, crates }); } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index caa7417011e..07a07983bd7 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -55,17 +55,6 @@ pub fn force_recompile(compiler: Compiler, target: TargetSelection) -> Self { } } -/// Given an `alias` selected by the `Step` and the paths passed on the command line, -/// return a list of the crates that should be built. -/// -/// Normally, people will pass *just* `library` if they pass it. -/// But it's possible (although strange) to pass something like `library std core`. -/// Build all crates anyway, as if they hadn't passed the other args. -pub(crate) fn make_run_crates(run: &RunConfig<'_>, alias: &str) -> Interned> { - let has_alias = run.paths.iter().any(|set| set.assert_single_path().path.ends_with(alias)); - if has_alias { Default::default() } else { run.cargo_crates_in_set() } -} - impl Step for Std { type Output = (); const DEFAULT: bool = true; @@ -80,10 +69,15 @@ fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { } fn make_run(run: RunConfig<'_>) { + // If the paths include "library", build the entire standard library. + let has_alias = + run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library")); + let crates = if has_alias { Default::default() } else { run.cargo_crates_in_set() }; + run.builder.ensure(Std { compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, - crates: make_run_crates(&run, "library"), + crates, force_recompile: false, }); } diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index b34a4b2dc63..2141bb0ddd9 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -106,7 +106,12 @@ fn make_run(run: RunConfig<'_>) { /// Builds the `rust-docs-json` installer component. fn run(self, builder: &Builder<'_>) -> Option { let host = self.host; - builder.ensure(crate::doc::Std::new(builder.top_stage, host, DocumentationFormat::JSON)); + builder.ensure(crate::doc::Std::new( + builder.top_stage, + host, + builder, + DocumentationFormat::JSON, + )); let dest = "share/doc/rust/json"; diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index 5ebfe0995a8..0fd6b46d562 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -11,10 +11,9 @@ use std::path::{Path, PathBuf}; use crate::builder::crate_description; -use crate::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; +use crate::builder::{Alias, Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; use crate::cache::{Interned, INTERNER}; use crate::compile; -use crate::compile::make_run_crates; use crate::config::{Config, TargetSelection}; use crate::tool::{self, prepare_tool_cargo, SourceType, Tool}; use crate::util::{symlink_dir, t, up_to_date}; @@ -424,8 +423,18 @@ pub struct Std { } impl Std { - pub(crate) fn new(stage: u32, target: TargetSelection, format: DocumentationFormat) -> Self { - Std { stage, target, format, crates: INTERNER.intern_list(vec![]) } + pub(crate) fn new( + stage: u32, + target: TargetSelection, + builder: &Builder<'_>, + format: DocumentationFormat, + ) -> Self { + let crates = builder + .in_tree_crates("sysroot", Some(target)) + .into_iter() + .map(|krate| krate.name.to_string()) + .collect(); + Std { stage, target, format, crates: INTERNER.intern_list(crates) } } } @@ -447,7 +456,7 @@ fn make_run(run: RunConfig<'_>) { } else { DocumentationFormat::HTML }, - crates: make_run_crates(&run, "library"), + crates: run.make_run_crates(Alias::Library), }); } @@ -455,7 +464,7 @@ fn make_run(run: RunConfig<'_>) { /// /// This will generate all documentation for the standard library and its /// dependencies. This is largely just a wrapper around `cargo doc`. - fn run(mut self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) { let stage = self.stage; let target = self.target; let out = match self.format { @@ -493,20 +502,17 @@ fn run(mut self, builder: &Builder<'_>) { return; } - // Look for library/std, library/core etc in the `x.py doc` arguments and - // open the corresponding rendered docs. - if self.crates.is_empty() { - self.crates = INTERNER.intern_list(vec!["library".to_owned()]); - }; - - for requested_crate in &*self.crates { - if requested_crate == "library" { - // For `x.py doc library --open`, open `std` by default. - let index = out.join("std").join("index.html"); - builder.open_in_browser(index); - } else if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) { - let index = out.join(requested_crate).join("index.html"); - builder.open_in_browser(index); + if builder.paths.iter().any(|path| path.ends_with("library")) { + // For `x.py doc library --open`, open `std` by default. + let index = out.join("std").join("index.html"); + builder.open_in_browser(index); + } else { + for requested_crate in &*self.crates { + if STD_PUBLIC_CRATES.iter().any(|&k| k == requested_crate) { + let index = out.join(requested_crate).join("index.html"); + builder.open_in_browser(index); + break; + } } } } @@ -539,9 +545,6 @@ fn as_str(&self) -> &str { } /// Build the documentation for public standard library crates. -/// -/// `requested_crates` can be used to build only a subset of the crates. If empty, all crates will -/// be built. fn doc_std( builder: &Builder<'_>, format: DocumentationFormat, @@ -592,19 +595,11 @@ fn doc_std( cargo.rustdocflag("--document-private-items").rustdocflag("--document-hidden-items"); } - // HACK: because we use `--manifest-path library/sysroot/Cargo.toml`, cargo thinks we only want to document that specific crate, not its dependencies. - // Override its default. - let built_crates = if requested_crates.is_empty() { - builder - .in_tree_crates("sysroot", None) - .into_iter() - .map(|krate| krate.name.to_string()) - .collect() - } else { - requested_crates.to_vec() - }; - - for krate in built_crates { + for krate in requested_crates { + if krate == "sysroot" { + // The sysroot crate is an implementation detail, don't include it in public docs. + continue; + } cargo.arg("-p").arg(krate); } @@ -621,20 +616,10 @@ pub struct Rustc { 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()) - }) + let crates = builder + .in_tree_crates("rustc-main", Some(target)) + .into_iter() + .map(|krate| krate.name.to_string()) .collect(); Self { stage, target, crates: INTERNER.intern_list(crates) } } @@ -656,7 +641,7 @@ fn make_run(run: RunConfig<'_>) { run.builder.ensure(Rustc { stage: run.builder.top_stage, target: run.target, - crates: make_run_crates(&run, "compiler"), + crates: run.make_run_crates(Alias::Compiler), }); } @@ -666,7 +651,7 @@ fn make_run(run: RunConfig<'_>) { /// Compiler documentation is distributed separately, so we make sure /// we do not merge it with the other documentation from std, test and /// proc_macros. This is largely just a wrapper around `cargo doc`. - fn run(mut self, builder: &Builder<'_>) { + fn run(self, builder: &Builder<'_>) { let stage = self.stage; let target = self.target; @@ -726,24 +711,26 @@ fn run(mut self, builder: &Builder<'_>) { let mut to_open = None; - if self.crates.is_empty() { - self.crates = INTERNER.intern_list(vec!["rustc_driver".to_owned()]); - }; - for krate in &*self.crates { // Create all crate output directories first to make sure rustdoc uses // relative links. // FIXME: Cargo should probably do this itself. - t!(fs::create_dir_all(out_dir.join(krate))); + let dir_name = krate.replace("-", "_"); + t!(fs::create_dir_all(out_dir.join(&*dir_name))); cargo.arg("-p").arg(krate); if to_open.is_none() { - to_open = Some(krate); + to_open = Some(dir_name); } } builder.run(&mut cargo.into()); - // Let's open the first crate documentation page: - if let Some(krate) = to_open { + + if builder.paths.iter().any(|path| path.ends_with("compiler")) { + // For `x.py doc compiler --open`, open `rustc_middle` by default. + let index = out.join("rustc_middle").join("index.html"); + builder.open_in_browser(index); + } else if let Some(krate) = to_open { + // Let's open the first crate documentation page: let index = out.join(krate).join("index.html"); builder.open_in_browser(index); } diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 0b509132043..f5ad4f336a7 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -1502,6 +1502,7 @@ fn in_tree_crates(&self, root: &str, target: Option) -> Vec<&Cr } } } + ret.sort_unstable_by_key(|krate| krate.name); // reproducible order needed for tests ret } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 3b8b837682d..284efff348d 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -901,6 +901,7 @@ fn run(self, builder: &Builder<'_>) { builder.ensure(crate::doc::Std::new( builder.top_stage, self.target, + builder, DocumentationFormat::HTML, )); builder.run(&mut command);