From 6d388a4ee33771784137d04186a5070df6f9f0cc Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 26 Dec 2022 19:00:26 -0600 Subject: [PATCH] Allow cleaning individual crates As a bonus, this stops special casing `clean` in `Builder`. --- src/bootstrap/builder.rs | 149 +++++++++++++++++++++++---------------- src/bootstrap/clean.rs | 77 +++++++++++++++++++- src/bootstrap/compile.rs | 15 +--- src/bootstrap/flags.rs | 10 +-- src/bootstrap/lib.rs | 4 -- 5 files changed, 166 insertions(+), 89 deletions(-) diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 72d6a48b37a..707e4169002 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -13,7 +13,6 @@ use std::time::{Duration, Instant}; use crate::cache::{Cache, Interned, INTERNER}; use crate::config::{SplitDebuginfo, TargetSelection}; -use crate::dist; use crate::doc; use crate::flags::{Color, Subcommand}; use crate::install; @@ -25,6 +24,7 @@ use crate::tool::{self, SourceType}; use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t}; use crate::EXTRA_CHECK_CFGS; use crate::{check, compile, Crate}; +use crate::{clean, dist}; use crate::{Build, CLang, DocTests, GitRepo, Mode}; pub use crate::Compiler; @@ -96,6 +96,17 @@ impl RunConfig<'_> { pub fn build_triple(&self) -> TargetSelection { self.builder.build.build } + + /// Return a `-p=x -p=y` string suitable for passing to a cargo invocation. + pub fn cargo_crates_in_set(&self) -> Interned> { + 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]; + crates.push(format!("-p={crate_name}")); + } + INTERNER.intern_list(crates) + } } struct StepDescription { @@ -764,8 +775,9 @@ impl<'a> Builder<'a> { run::GenerateCopyright, ), Kind::Setup => describe!(setup::Profile), - // These commands either don't use paths, or they're special-cased in Build::build() - Kind::Clean | Kind::Format => vec![], + Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std), + // special-cased in Build::build() + Kind::Format => vec![], } } @@ -827,14 +839,12 @@ impl<'a> Builder<'a> { Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]), Subcommand::Install { ref paths } => (Kind::Install, &paths[..]), Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]), + Subcommand::Clean { ref paths, .. } => (Kind::Clean, &paths[..]), Subcommand::Format { .. } => (Kind::Format, &[][..]), Subcommand::Setup { profile: ref path } => ( Kind::Setup, path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)), ), - Subcommand::Clean { .. } => { - panic!() - } }; Self::new_internal(build, kind, paths.to_owned()) @@ -1077,64 +1087,22 @@ impl<'a> Builder<'a> { None } - /// Prepares an invocation of `cargo` to be run. - /// - /// This will create a `Command` that represents a pending execution of - /// Cargo. This cargo will be configured to use `compiler` as the actual - /// rustc compiler, its output will be scoped by `mode`'s output directory, - /// it will pass the `--target` flag for the specified `target`, and will be - /// executing the Cargo command `cmd`. - pub fn cargo( + /// Like `cargo`, but only passes flags that are valid for all commands. + pub fn bare_cargo( &self, compiler: Compiler, mode: Mode, - source_type: SourceType, target: TargetSelection, cmd: &str, - ) -> Cargo { + ) -> Command { let mut cargo = Command::new(&self.initial_cargo); - let out_dir = self.stage_out(compiler, mode); // Run cargo from the source root so it can find .cargo/config. // This matters when using vendoring and the working directory is outside the repository. cargo.current_dir(&self.src); - // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, - // so we need to explicitly clear out if they've been updated. - for backend in self.codegen_backends(compiler) { - self.clear_if_dirty(&out_dir, &backend); - } - - if cmd == "doc" || cmd == "rustdoc" { - let my_out = match mode { - // This is the intended out directory for compiler documentation. - Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), - Mode::Std => { - if self.config.cmd.json() { - out_dir.join(target.triple).join("json-doc") - } else { - out_dir.join(target.triple).join("doc") - } - } - _ => panic!("doc mode {:?} not expected", mode), - }; - let rustdoc = self.rustdoc(compiler); - self.clear_if_dirty(&my_out, &rustdoc); - } - + let out_dir = self.stage_out(compiler, mode); cargo.env("CARGO_TARGET_DIR", &out_dir).arg(cmd); - let profile_var = |name: &str| { - let profile = if self.config.rust_optimize { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) - }; - - // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config - // needs to not accidentally link to libLLVM in stage0/lib. - cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var()); - if let Some(e) = env::var_os(util::dylib_path_var()) { - cargo.env("REAL_LIBRARY_PATH", e); - } - // Found with `rg "init_env_logger\("`. If anyone uses `init_env_logger` // from out of tree it shouldn't matter, since x.py is only used for // building in-tree. @@ -1161,6 +1129,73 @@ impl<'a> Builder<'a> { assert_eq!(target, compiler.host); } + if self.config.rust_optimize { + // FIXME: cargo bench/install do not accept `--release` + if cmd != "bench" && cmd != "install" { + cargo.arg("--release"); + } + } + + // Remove make-related flags to ensure Cargo can correctly set things up + cargo.env_remove("MAKEFLAGS"); + cargo.env_remove("MFLAGS"); + + cargo + } + + /// Prepares an invocation of `cargo` to be run. + /// + /// This will create a `Command` that represents a pending execution of + /// Cargo. This cargo will be configured to use `compiler` as the actual + /// rustc compiler, its output will be scoped by `mode`'s output directory, + /// it will pass the `--target` flag for the specified `target`, and will be + /// executing the Cargo command `cmd`. + pub fn cargo( + &self, + compiler: Compiler, + mode: Mode, + source_type: SourceType, + target: TargetSelection, + cmd: &str, + ) -> Cargo { + let mut cargo = self.bare_cargo(compiler, mode, target, cmd); + let out_dir = self.stage_out(compiler, mode); + + // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, + // so we need to explicitly clear out if they've been updated. + for backend in self.codegen_backends(compiler) { + self.clear_if_dirty(&out_dir, &backend); + } + + if cmd == "doc" || cmd == "rustdoc" { + let my_out = match mode { + // This is the intended out directory for compiler documentation. + Mode::Rustc | Mode::ToolRustc => self.compiler_doc_out(target), + Mode::Std => { + if self.config.cmd.json() { + out_dir.join(target.triple).join("json-doc") + } else { + out_dir.join(target.triple).join("doc") + } + } + _ => panic!("doc mode {:?} not expected", mode), + }; + let rustdoc = self.rustdoc(compiler); + self.clear_if_dirty(&my_out, &rustdoc); + } + + let profile_var = |name: &str| { + let profile = if self.config.rust_optimize { "RELEASE" } else { "DEV" }; + format!("CARGO_PROFILE_{}_{}", profile, name) + }; + + // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config + // needs to not accidentally link to libLLVM in stage0/lib. + cargo.env("REAL_LIBRARY_PATH_VAR", &util::dylib_path_var()); + if let Some(e) = env::var_os(util::dylib_path_var()) { + cargo.env("REAL_LIBRARY_PATH", e); + } + // Set a flag for `check`/`clippy`/`fix`, so that certain build // scripts can do less work (i.e. not building/requiring LLVM). if cmd == "check" || cmd == "clippy" || cmd == "fix" { @@ -1341,9 +1376,6 @@ impl<'a> Builder<'a> { } cargo.arg("-j").arg(self.jobs().to_string()); - // Remove make-related flags to ensure Cargo can correctly set things up - cargo.env_remove("MAKEFLAGS"); - cargo.env_remove("MFLAGS"); // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005 // Force cargo to output binaries with disambiguating hashes in the name @@ -1827,13 +1859,6 @@ impl<'a> Builder<'a> { } } - if self.config.rust_optimize { - // FIXME: cargo bench/install do not accept `--release` - if cmd != "bench" && cmd != "install" { - cargo.arg("--release"); - } - } - if self.config.locked_deps { cargo.arg("--locked"); } diff --git a/src/bootstrap/clean.rs b/src/bootstrap/clean.rs index 069f3d6acf1..303c5603be7 100644 --- a/src/bootstrap/clean.rs +++ b/src/bootstrap/clean.rs @@ -9,10 +9,83 @@ use std::fs; use std::io::{self, ErrorKind}; use std::path::Path; +use crate::builder::{Builder, RunConfig, ShouldRun, Step}; +use crate::cache::Interned; +use crate::config::TargetSelection; use crate::util::t; -use crate::Build; +use crate::{Build, Mode, Subcommand}; -pub fn clean(build: &Build, all: bool) { +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct CleanAll {} + +impl Step for CleanAll { + const DEFAULT: bool = true; + type Output = (); + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(CleanAll {}) + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let Subcommand::Clean { all, .. } = builder.config.cmd else { unreachable!("wrong subcommand?") }; + clean_default(builder.build, all) + } + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.never() // handled by DEFAULT + } +} + +macro_rules! clean_crate_tree { + ( $( $name:ident, $mode:path, $root_crate:literal);+ $(;)? ) => { $( + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name { + target: TargetSelection, + crates: Interned>, + } + + impl Step for $name { + type Output = (); + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let crates = run.builder.in_tree_crates($root_crate, None); + run.crates(crates) + } + + fn make_run(run: RunConfig<'_>) { + let builder = run.builder; + if builder.top_stage != 0 { + panic!("non-stage-0 clean not supported for individual crates"); + } + builder.ensure(Self { crates: run.cargo_crates_in_set(), target: run.target }); + } + + fn run(self, builder: &Builder<'_>) -> Self::Output { + let compiler = builder.compiler(0, self.target); + let mut cargo = builder.bare_cargo(compiler, $mode, self.target, "clean"); + for krate in &*self.crates { + cargo.arg(krate); + } + + builder.info(&format!( + "Cleaning stage{} {} artifacts ({} -> {})", + compiler.stage, stringify!($name).to_lowercase(), &compiler.host, self.target + )); + + // NOTE: doesn't use `run_cargo` because we don't want to save a stamp file, + // and doesn't use `stream_cargo` to avoid passing `--message-format` which `clean` doesn't accept. + builder.run(&mut cargo); + } + } + )+ } +} + +clean_crate_tree! { + Rustc, Mode::Rustc, "rustc-main"; + Std, Mode::Std, "test"; +} + +fn clean_default(build: &Build, all: bool) { rm_rf("tmp".as_ref()); if all { diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 0e3bbad9909..427e1a3863e 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -46,17 +46,6 @@ impl Std { } } -/// Return a `-p=x -p=y` string suitable for passing to a cargo invocation. -fn build_crates_in_set(run: &RunConfig<'_>) -> Interned> { - let mut crates = Vec::new(); - for krate in &run.paths { - let path = krate.assert_single_path(); - let crate_name = run.builder.crate_paths[&path.path]; - crates.push(format!("-p={crate_name}")); - } - INTERNER.intern_list(crates) -} - impl Step for Std { type Output = (); const DEFAULT: bool = true; @@ -76,7 +65,7 @@ impl Step for Std { // 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 { build_crates_in_set(&run) }; + 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, @@ -603,7 +592,7 @@ impl Step for Rustc { } fn make_run(run: RunConfig<'_>) { - let crates = build_crates_in_set(&run); + let crates = run.cargo_crates_in_set(); run.builder.ensure(Rustc { compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), target: run.target, diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 851cb5ecf4c..459d2fa964d 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -130,6 +130,7 @@ pub enum Subcommand { test_args: Vec, }, Clean { + paths: Vec, all: bool, }, Dist { @@ -601,14 +602,7 @@ Arguments: open: matches.opt_present("open"), json: matches.opt_present("json"), }, - Kind::Clean => { - if !paths.is_empty() { - println!("\nclean does not take a path argument\n"); - usage(1, &opts, verbose, &subcommand_help); - } - - Subcommand::Clean { all: matches.opt_present("all") } - } + Kind::Clean => Subcommand::Clean { all: matches.opt_present("all"), paths }, Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths }, Kind::Dist => Subcommand::Dist { paths }, Kind::Install => Subcommand::Install { paths }, diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index f84fcd21cfc..f3e6da8e3e0 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -727,10 +727,6 @@ impl Build { return format::format(&builder::Builder::new(&self), *check, &paths); } - if let Subcommand::Clean { all } = self.config.cmd { - return clean::clean(self, all); - } - // Download rustfmt early so that it can be used in rust-analyzer configs. let _ = &builder::Builder::new(&self).initial_rustfmt();