Skip to content

Commit b38a6d3

Browse files
committed
Auto merge of #106168 - jyn514:clean-crates, r=Mark-Simulacrum
Allow cleaning individual crates As a bonus, this stops special casing `clean` in `Builder`. ## Motivation Cleaning artifacts isn't strictly necessary to get cargo to rebuild; `touch compiler/rustc_driver/src/lib.rs` (for example) will also work. There's two reasons I thought making this part of bootstrap proper was a better approach: 1. `touch` does not *remove* artifacts, it just causes a rebuild. This is unhelpful for when you want to measure how long the compiler itself takes to build (e.g. for #65031). 2. It seems a little more discoverable; and I want to extend it in the future to things like `x clean --stage 1 rustc`, which makes it easier to work around #76720 without having to completely wipe all the stage 0 artifacts, or having to be intimately familiar with which directories to remove.
2 parents a1fc711 + 6d388a4 commit b38a6d3

File tree

5 files changed

+152
-75
lines changed

5 files changed

+152
-75
lines changed

src/bootstrap/builder.rs

+73-48
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use std::time::{Duration, Instant};
1313

1414
use crate::cache::{Cache, Interned, INTERNER};
1515
use crate::config::{SplitDebuginfo, TargetSelection};
16-
use crate::dist;
1716
use crate::doc;
1817
use crate::flags::{Color, Subcommand};
1918
use crate::install;
@@ -25,6 +24,7 @@ use crate::tool::{self, SourceType};
2524
use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
2625
use crate::EXTRA_CHECK_CFGS;
2726
use crate::{check, compile, Crate};
27+
use crate::{clean, dist};
2828
use crate::{Build, CLang, DocTests, GitRepo, Mode};
2929

3030
pub use crate::Compiler;
@@ -96,6 +96,17 @@ impl RunConfig<'_> {
9696
pub fn build_triple(&self) -> TargetSelection {
9797
self.builder.build.build
9898
}
99+
100+
/// Return a `-p=x -p=y` string suitable for passing to a cargo invocation.
101+
pub fn cargo_crates_in_set(&self) -> Interned<Vec<String>> {
102+
let mut crates = Vec::new();
103+
for krate in &self.paths {
104+
let path = krate.assert_single_path();
105+
let crate_name = self.builder.crate_paths[&path.path];
106+
crates.push(format!("-p={crate_name}"));
107+
}
108+
INTERNER.intern_list(crates)
109+
}
99110
}
100111

101112
struct StepDescription {
@@ -764,8 +775,9 @@ impl<'a> Builder<'a> {
764775
run::GenerateCopyright,
765776
),
766777
Kind::Setup => describe!(setup::Profile),
767-
// These commands either don't use paths, or they're special-cased in Build::build()
768-
Kind::Clean | Kind::Format => vec![],
778+
Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
779+
// special-cased in Build::build()
780+
Kind::Format => vec![],
769781
}
770782
}
771783

@@ -827,14 +839,12 @@ impl<'a> Builder<'a> {
827839
Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
828840
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
829841
Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]),
842+
Subcommand::Clean { ref paths, .. } => (Kind::Clean, &paths[..]),
830843
Subcommand::Format { .. } => (Kind::Format, &[][..]),
831844
Subcommand::Setup { profile: ref path } => (
832845
Kind::Setup,
833846
path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
834847
),
835-
Subcommand::Clean { .. } => {
836-
panic!()
837-
}
838848
};
839849

840850
Self::new_internal(build, kind, paths.to_owned())
@@ -1077,6 +1087,62 @@ impl<'a> Builder<'a> {
10771087
None
10781088
}
10791089

1090+
/// Like `cargo`, but only passes flags that are valid for all commands.
1091+
pub fn bare_cargo(
1092+
&self,
1093+
compiler: Compiler,
1094+
mode: Mode,
1095+
target: TargetSelection,
1096+
cmd: &str,
1097+
) -> Command {
1098+
let mut cargo = Command::new(&self.initial_cargo);
1099+
// Run cargo from the source root so it can find .cargo/config.
1100+
// This matters when using vendoring and the working directory is outside the repository.
1101+
cargo.current_dir(&self.src);
1102+
1103+
let out_dir = self.stage_out(compiler, mode);
1104+
cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd);
1105+
1106+
// Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
1107+
// from out of tree it shouldn't matter, since x.py is only used for
1108+
// building in-tree.
1109+
let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
1110+
match self.build.config.color {
1111+
Color::Always => {
1112+
cargo.arg("--color=always");
1113+
for log in &color_logs {
1114+
cargo.env(log, "always");
1115+
}
1116+
}
1117+
Color::Never => {
1118+
cargo.arg("--color=never");
1119+
for log in &color_logs {
1120+
cargo.env(log, "never");
1121+
}
1122+
}
1123+
Color::Auto => {} // nothing to do
1124+
}
1125+
1126+
if cmd != "install" {
1127+
cargo.arg("--target").arg(target.rustc_target_arg());
1128+
} else {
1129+
assert_eq!(target, compiler.host);
1130+
}
1131+
1132+
if self.config.rust_optimize {
1133+
// FIXME: cargo bench/install do not accept `--release`
1134+
if cmd != "bench" && cmd != "install" {
1135+
cargo.arg("--release");
1136+
}
1137+
}
1138+
1139+
// Remove make-related flags to ensure Cargo can correctly set things up
1140+
cargo.env_remove("MAKEFLAGS");
1141+
cargo.env_remove("MFLAGS");
1142+
1143+
cargo
1144+
}
1145+
10801146
/// Prepares an invocation of `cargo` to be run.
10811147
///
10821148
/// This will create a `Command` that represents a pending execution of
@@ -1092,11 +1158,8 @@ impl<'a> Builder<'a> {
10921158
target: TargetSelection,
10931159
cmd: &str,
10941160
) -> Cargo {
1095-
let mut cargo = Command::new(&self.initial_cargo);
1161+
let mut cargo = self.bare_cargo(compiler, mode, target, cmd);
10961162
let out_dir = self.stage_out(compiler, mode);
1097-
// Run cargo from the source root so it can find .cargo/config.
1098-
// This matters when using vendoring and the working directory is outside the repository.
1099-
cargo.current_dir(&self.src);
11001163

11011164
// Codegen backends are not yet tracked by -Zbinary-dep-depinfo,
11021165
// so we need to explicitly clear out if they've been updated.
@@ -1121,8 +1184,6 @@ impl<'a> Builder<'a> {
11211184
self.clear_if_dirty(&my_out, &rustdoc);
11221185
}
11231186

1124-
cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd);
1125-
11261187
let profile_var = |name: &str| {
11271188
let profile = if self.config.rust_optimize { "RELEASE" } else { "DEV" };
11281189
format!("CARGO_PROFILE_{}_{}", profile, name)
@@ -1135,32 +1196,6 @@ impl<'a> Builder<'a> {
11351196
cargo.env("REAL_LIBRARY_PATH", e);
11361197
}
11371198

1138-
// Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger`
1139-
// from out of tree it shouldn't matter, since x.py is only used for
1140-
// building in-tree.
1141-
let color_logs = ["RUSTDOC_LOG_COLOR", "RUSTC_LOG_COLOR", "RUST_LOG_COLOR"];
1142-
match self.build.config.color {
1143-
Color::Always => {
1144-
cargo.arg("--color=always");
1145-
for log in &color_logs {
1146-
cargo.env(log, "always");
1147-
}
1148-
}
1149-
Color::Never => {
1150-
cargo.arg("--color=never");
1151-
for log in &color_logs {
1152-
cargo.env(log, "never");
1153-
}
1154-
}
1155-
Color::Auto => {} // nothing to do
1156-
}
1157-
1158-
if cmd != "install" {
1159-
cargo.arg("--target").arg(target.rustc_target_arg());
1160-
} else {
1161-
assert_eq!(target, compiler.host);
1162-
}
1163-
11641199
// Set a flag for `check`/`clippy`/`fix`, so that certain build
11651200
// scripts can do less work (i.e. not building/requiring LLVM).
11661201
if cmd == "check" || cmd == "clippy" || cmd == "fix" {
@@ -1341,9 +1376,6 @@ impl<'a> Builder<'a> {
13411376
}
13421377

13431378
cargo.arg("-j").arg(self.jobs().to_string());
1344-
// Remove make-related flags to ensure Cargo can correctly set things up
1345-
cargo.env_remove("MAKEFLAGS");
1346-
cargo.env_remove("MFLAGS");
13471379

13481380
// FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
13491381
// Force cargo to output binaries with disambiguating hashes in the name
@@ -1827,13 +1859,6 @@ impl<'a> Builder<'a> {
18271859
}
18281860
}
18291861

1830-
if self.config.rust_optimize {
1831-
// FIXME: cargo bench/install do not accept `--release`
1832-
if cmd != "bench" && cmd != "install" {
1833-
cargo.arg("--release");
1834-
}
1835-
}
1836-
18371862
if self.config.locked_deps {
18381863
cargo.arg("--locked");
18391864
}

src/bootstrap/clean.rs

+75-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,83 @@ use std::fs;
99
use std::io::{self, ErrorKind};
1010
use std::path::Path;
1111

12+
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
13+
use crate::cache::Interned;
14+
use crate::config::TargetSelection;
1215
use crate::util::t;
13-
use crate::Build;
16+
use crate::{Build, Mode, Subcommand};
1417

15-
pub fn clean(build: &Build, all: bool) {
18+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19+
pub struct CleanAll {}
20+
21+
impl Step for CleanAll {
22+
const DEFAULT: bool = true;
23+
type Output = ();
24+
25+
fn make_run(run: RunConfig<'_>) {
26+
run.builder.ensure(CleanAll {})
27+
}
28+
29+
fn run(self, builder: &Builder<'_>) -> Self::Output {
30+
let Subcommand::Clean { all, .. } = builder.config.cmd else { unreachable!("wrong subcommand?") };
31+
clean_default(builder.build, all)
32+
}
33+
34+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
35+
run.never() // handled by DEFAULT
36+
}
37+
}
38+
39+
macro_rules! clean_crate_tree {
40+
( $( $name:ident, $mode:path, $root_crate:literal);+ $(;)? ) => { $(
41+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42+
pub struct $name {
43+
target: TargetSelection,
44+
crates: Interned<Vec<String>>,
45+
}
46+
47+
impl Step for $name {
48+
type Output = ();
49+
50+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
51+
let crates = run.builder.in_tree_crates($root_crate, None);
52+
run.crates(crates)
53+
}
54+
55+
fn make_run(run: RunConfig<'_>) {
56+
let builder = run.builder;
57+
if builder.top_stage != 0 {
58+
panic!("non-stage-0 clean not supported for individual crates");
59+
}
60+
builder.ensure(Self { crates: run.cargo_crates_in_set(), target: run.target });
61+
}
62+
63+
fn run(self, builder: &Builder<'_>) -> Self::Output {
64+
let compiler = builder.compiler(0, self.target);
65+
let mut cargo = builder.bare_cargo(compiler, $mode, self.target, "clean");
66+
for krate in &*self.crates {
67+
cargo.arg(krate);
68+
}
69+
70+
builder.info(&format!(
71+
"Cleaning stage{} {} artifacts ({} -> {})",
72+
compiler.stage, stringify!($name).to_lowercase(), &compiler.host, self.target
73+
));
74+
75+
// NOTE: doesn't use `run_cargo` because we don't want to save a stamp file,
76+
// and doesn't use `stream_cargo` to avoid passing `--message-format` which `clean` doesn't accept.
77+
builder.run(&mut cargo);
78+
}
79+
}
80+
)+ }
81+
}
82+
83+
clean_crate_tree! {
84+
Rustc, Mode::Rustc, "rustc-main";
85+
Std, Mode::Std, "test";
86+
}
87+
88+
fn clean_default(build: &Build, all: bool) {
1689
rm_rf("tmp".as_ref());
1790

1891
if all {

src/bootstrap/compile.rs

+2-13
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,6 @@ impl Std {
4646
}
4747
}
4848

49-
/// Return a `-p=x -p=y` string suitable for passing to a cargo invocation.
50-
fn build_crates_in_set(run: &RunConfig<'_>) -> Interned<Vec<String>> {
51-
let mut crates = Vec::new();
52-
for krate in &run.paths {
53-
let path = krate.assert_single_path();
54-
let crate_name = run.builder.crate_paths[&path.path];
55-
crates.push(format!("-p={crate_name}"));
56-
}
57-
INTERNER.intern_list(crates)
58-
}
59-
6049
impl Step for Std {
6150
type Output = ();
6251
const DEFAULT: bool = true;
@@ -76,7 +65,7 @@ impl Step for Std {
7665
// Build all crates anyway, as if they hadn't passed the other args.
7766
let has_library =
7867
run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library"));
79-
let crates = if has_library { Default::default() } else { build_crates_in_set(&run) };
68+
let crates = if has_library { Default::default() } else { run.cargo_crates_in_set() };
8069
run.builder.ensure(Std {
8170
compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
8271
target: run.target,
@@ -603,7 +592,7 @@ impl Step for Rustc {
603592
}
604593

605594
fn make_run(run: RunConfig<'_>) {
606-
let crates = build_crates_in_set(&run);
595+
let crates = run.cargo_crates_in_set();
607596
run.builder.ensure(Rustc {
608597
compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
609598
target: run.target,

src/bootstrap/flags.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ pub enum Subcommand {
130130
test_args: Vec<String>,
131131
},
132132
Clean {
133+
paths: Vec<PathBuf>,
133134
all: bool,
134135
},
135136
Dist {
@@ -611,14 +612,7 @@ Arguments:
611612
open: matches.opt_present("open"),
612613
json: matches.opt_present("json"),
613614
},
614-
Kind::Clean => {
615-
if !paths.is_empty() {
616-
println!("\nclean does not take a path argument\n");
617-
usage(1, &opts, verbose, &subcommand_help);
618-
}
619-
620-
Subcommand::Clean { all: matches.opt_present("all") }
621-
}
615+
Kind::Clean => Subcommand::Clean { all: matches.opt_present("all"), paths },
622616
Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths },
623617
Kind::Dist => Subcommand::Dist { paths },
624618
Kind::Install => Subcommand::Install { paths },

src/bootstrap/lib.rs

-4
Original file line numberDiff line numberDiff line change
@@ -727,10 +727,6 @@ impl Build {
727727
return format::format(&builder::Builder::new(&self), *check, &paths);
728728
}
729729

730-
if let Subcommand::Clean { all } = self.config.cmd {
731-
return clean::clean(self, all);
732-
}
733-
734730
// Download rustfmt early so that it can be used in rust-analyzer configs.
735731
let _ = &builder::Builder::new(&self).initial_rustfmt();
736732

0 commit comments

Comments
 (0)