Skip to content

bootstrap: Various Step refactors #111955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 30, 2023
44 changes: 21 additions & 23 deletions src/bootstrap/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,14 @@ impl RunConfig<'_> {
}

/// Return a list of crate names selected by `run.paths`.
#[track_caller]
pub fn cargo_crates_in_set(&self) -> Interned<Vec<String>> {
let mut crates = Vec::new();
for krate in &self.paths {
let path = krate.assert_single_path();
let crate_name = self.builder.crate_paths[&path.path];
let Some(crate_name) = self.builder.crate_paths.get(&path.path) else {
panic!("missing crate for path {}", path.path.display())
};
crates.push(crate_name.to_string());
}
INTERNER.intern_list(crates)
Expand Down Expand Up @@ -427,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
/// any of its (local) dependencies.
///
Expand All @@ -458,6 +442,8 @@ impl<'a> ShouldRun<'a> {
/// Indicates it should run if the command-line selects any of the given crates.
///
/// `make_run` will be called a single time with all matching command-line paths.
///
/// Prefer [`ShouldRun::crate_or_deps`] to this function where possible.
pub(crate) fn crates(mut self, crates: Vec<&Crate>) -> Self {
for krate in crates {
let path = krate.local_path(self.builder);
Expand Down Expand Up @@ -487,7 +473,15 @@ impl<'a> ShouldRun<'a> {
self.paths(&[path])
}

// multiple aliases for the same job
/// Multiple aliases for the same job.
///
/// This differs from [`path`] in that multiple calls to path will end up calling `make_run`
/// multiple times, whereas a single call to `paths` will only ever generate a single call to
/// `paths`.
Comment on lines +479 to +480
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"a single call to paths will only ever generate a single call to paths" eh... what does that mean?^^

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, this is a blast from the past. probably i meant "a single call to make_run", by analogy with the docs above? i honestly don't remember though, it's been ages.

///
/// This is analogous to `all_krates`, although `all_krates` is gone now. Prefer [`path`] where possible.
///
/// [`path`]: ShouldRun::path
pub fn paths(mut self, paths: &[&str]) -> Self {
static SUBMODULES_PATHS: OnceCell<Vec<String>> = OnceCell::new();

Expand Down Expand Up @@ -641,12 +635,16 @@ impl Kind {
}
}

pub fn test_description(&self) -> &'static str {
pub fn description(&self) -> String {
match self {
Kind::Test => "Testing",
Kind::Bench => "Benchmarking",
_ => panic!("not a test command: {}!", self.as_str()),
Kind::Doc => "Documenting",
Kind::Run => "Running",
Kind::Suggest => "Suggesting",
_ => return format!("{self:?}"),
}
.to_owned()
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/bootstrap/builder/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;
use crate::config::{Config, DryRun, TargetSelection};
use crate::doc::DocumentationFormat;
use std::thread;

fn configure(cmd: &str, host: &[&str], target: &[&str]) -> Config {
Expand Down Expand Up @@ -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 {
($host:ident => $target:ident, stage = $stage:literal) => {
compile::Rustc::new(
Expand Down Expand Up @@ -144,6 +155,9 @@ fn alias_and_path_for_library() {
first(cache.all::<compile::Std>()),
&[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]
Expand Down
79 changes: 61 additions & 18 deletions src/bootstrap/check.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Implementation of compiling the compiler and standard library, in "check"-based modes.

use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
use crate::builder::{crate_description, Builder, Kind, RunConfig, ShouldRun, Step};
use crate::cache::Interned;
use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo};
use crate::compile::{
add_to_sysroot, make_run_crates, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo,
};
use crate::config::TargetSelection;
use crate::tool::{prepare_tool_cargo, SourceType};
use crate::INTERNER;
Expand All @@ -12,6 +14,12 @@ use std::path::{Path, PathBuf};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Std {
pub target: TargetSelection,
/// Whether to build only a subset of crates.
///
/// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
///
/// [`compile::Rustc`]: crate::compile::Rustc
crates: Interned<Vec<String>>,
}

/// Returns args for the subcommand itself (not for cargo)
Expand Down Expand Up @@ -66,16 +74,23 @@ fn cargo_subcommand(kind: Kind) -> &'static str {
}
}

impl Std {
pub fn new(target: TargetSelection) -> Self {
Self { target, crates: INTERNER.intern_list(vec![]) }
}
}

impl Step for Std {
type Output = ();
const DEFAULT: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.all_krates("sysroot").path("library")
run.crate_or_deps("sysroot").path("library")
}

fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Std { target: run.target });
let crates = make_run_crates(&run, "library");
run.builder.ensure(Std { target: run.target, crates });
}

fn run(self, builder: &Builder<'_>) {
Expand All @@ -97,7 +112,14 @@ impl Step for Std {
cargo.arg("--lib");
}

let _guard = builder.msg_check("library artifacts", target);
for krate in &*self.crates {
cargo.arg("-p").arg(krate);
}

let _guard = builder.msg_check(
format_args!("library artifacts{}", crate_description(&self.crates)),
target,
);
run_cargo(
builder,
cargo,
Expand All @@ -117,7 +139,8 @@ impl Step for Std {
}

// don't run on std twice with x.py clippy
if builder.kind == Kind::Clippy {
// don't check test dependencies if we haven't built libtest
if builder.kind == Kind::Clippy || !self.crates.is_empty() {
return;
}

Expand Down Expand Up @@ -147,8 +170,8 @@ impl Step for Std {
// Explicitly pass -p for all dependencies krates -- this will force cargo
// to also check the tests/benches/examples for these crates, rather
// than just the leaf crate.
for krate in builder.in_tree_crates("test", Some(target)) {
cargo.arg("-p").arg(krate.name);
for krate in &*self.crates {
cargo.arg("-p").arg(krate);
}

let _guard = builder.msg_check("library test/bench/example targets", target);
Expand All @@ -167,6 +190,22 @@ impl Step for Std {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Rustc {
pub target: TargetSelection,
/// Whether to build only a subset of crates.
///
/// This shouldn't be used from other steps; see the comment on [`compile::Rustc`].
///
/// [`compile::Rustc`]: crate::compile::Rustc
crates: Interned<Vec<String>>,
}

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());
}
Self { target, crates: INTERNER.intern_list(crates) }
}
}

impl Step for Rustc {
Expand All @@ -175,11 +214,12 @@ impl Step for Rustc {
const DEFAULT: bool = true;

fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
run.all_krates("rustc-main").path("compiler")
run.crate_or_deps("rustc-main").path("compiler")
}

fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustc { target: run.target });
let crates = make_run_crates(&run, "compiler");
run.builder.ensure(Rustc { target: run.target, crates });
}

/// Builds the compiler.
Expand All @@ -200,7 +240,7 @@ impl Step for Rustc {
builder.ensure(crate::compile::Std::new(compiler, compiler.host));
builder.ensure(crate::compile::Std::new(compiler, target));
} else {
builder.ensure(Std { target });
builder.ensure(Std::new(target));
}

let mut cargo = builder.cargo(
Expand All @@ -218,14 +258,17 @@ impl Step for Rustc {
cargo.arg("--all-targets");
}

// Explicitly pass -p for all compiler krates -- this will force cargo
// Explicitly pass -p for all compiler crates -- this will force cargo
// to also check the tests/benches/examples for these crates, rather
// than just the leaf crate.
for krate in builder.in_tree_crates("rustc-main", Some(target)) {
cargo.arg("-p").arg(krate.name);
for krate in &*self.crates {
cargo.arg("-p").arg(krate);
}

let _guard = builder.msg_check("compiler artifacts", target);
let _guard = builder.msg_check(
format_args!("compiler artifacts{}", crate_description(&self.crates)),
target,
);
run_cargo(
builder,
cargo,
Expand Down Expand Up @@ -268,7 +311,7 @@ impl Step for CodegenBackend {
let target = self.target;
let backend = self.backend;

builder.ensure(Rustc { target });
builder.ensure(Rustc::new(target, builder));

let mut cargo = builder.cargo(
compiler,
Expand Down Expand Up @@ -318,7 +361,7 @@ impl Step for RustAnalyzer {
let compiler = builder.compiler(builder.top_stage, builder.config.build);
let target = self.target;

builder.ensure(Std { target });
builder.ensure(Std::new(target));

let mut cargo = prepare_tool_cargo(
builder,
Expand Down Expand Up @@ -386,7 +429,7 @@ macro_rules! tool_check_step {
let compiler = builder.compiler(builder.top_stage, builder.config.build);
let target = self.target;

builder.ensure(Rustc { target });
builder.ensure(Rustc::new(target, builder));

let mut cargo = prepare_tool_cargo(
builder,
Expand Down
21 changes: 14 additions & 7 deletions src/bootstrap/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ impl Std {
}
}

/// 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<Vec<String>> {
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;
Expand All @@ -62,16 +73,10 @@ impl Step for Std {
}

fn make_run(run: RunConfig<'_>) {
// 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.
let has_library =
run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library"));
let crates = if has_library { 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,
crates: make_run_crates(&run, "library"),
});
}

Expand Down Expand Up @@ -615,6 +620,8 @@ impl Step for Rustc {
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let mut crates = run.builder.in_tree_crates("rustc-main", None);
for (i, krate) in crates.iter().enumerate() {
// We can't allow `build rustc` as an alias for this Step, because that's reserved by `Assemble`.
// Ideally Assemble would use `build compiler` instead, but that seems too confusing to be worth the breaking change.
if krate.name == "rustc-main" {
crates.swap_remove(i);
break;
Expand Down
6 changes: 1 addition & 5 deletions src/bootstrap/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ impl Step for JsonDocs {
/// Builds the `rust-docs-json` installer component.
fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
let host = self.host;
builder.ensure(crate::doc::Std {
stage: builder.top_stage,
target: host,
format: DocumentationFormat::JSON,
});
builder.ensure(crate::doc::Std::new(builder.top_stage, host, DocumentationFormat::JSON));

let dest = "share/doc/rust/json";

Expand Down
Loading