Change:
When running two times in a row, the target as well as rls will be built again for the second even if no sources were touched.
Hm, this is an unexpected consequence of . Clippy gets built with different RUSTFLAGS, which busts the cache. I'm going to contemplate finding some way to independently cache artifacts with different RUSTFLAGS, it has come up multiple times recently. In the meantime, we may want to consider changing Clippy back to "Submodule" type so that it doesn't use different RUSTFLAGS.
I would've expected x.py build without arguments to be relatively rare as something folks are running. However, if this is an active blocker/pain point (and not just "mild annoyance"), I'm okay with adding an exception for clippy (I'd rather not change its type as that may have effects elsewhere and I wouldn't want to have to think about it every time until this is fixed). Separate caches seems reasonable, though would only contribute to perceived "bloat" of target directories I imagine.
It increases CI time because some dependencies are getting built multiple times. I compared the build just before , and it looks like it may have 18 minutes to the build time. I think it is relatively safe to change clippy to Submodule mode. The only difference is "deny warnings". The risk is that a rust-lang/rust PR introduces a new warning in the clippy code, which will have to be resolved whenever clippy is synced back to its repo. Of course it would be better to deny warnings, just tossing it out as an option. (Just to be clear, I'm not certain that's the only issue here, it will take some more investigation and testing to verify.)
Ah, so perhaps I misinterpreted -- I believe most of our CI platforms only run x.py once? (At least the longer ones, where a bump in CI times really matters). Yeah, I agree that there's not much difference to changing to submodule mode vs. explicitly gating on clippy in the warnings check, but I would prefer the later I think. I would loosely be prepared to r+ such a PR.
The cache thrashing is due to the order the tools are built. It goes: -driver (busts cache, lots of rebuilds) (lots of rebuilds due to 3) -clippy (busts cache, lots of rebuilds) (some rebuilds due to 5) I dunno how bootstrap decides which order things get built. Is it random? I'm surprised the two clippy binaries are not done consecutively.
they are built in this order: In the past when Clippy was not always buildable I had since RLS's feature works only when Clippy can build. That's why Clippy driver is built before RLS. Reordering tools in would remove 1 rebuild, right?

Discussion:
cmd.env("RUST_BACKTRACE", "1"); } <ins> if let Ok(lint_flags) = env::var("RUSTC_LINT_FLAGS") { cmd.args(lint_flags.split_whitespace()); } </ins> if target.is_some() { // The stage0 compiler has a special sysroot distinct from what we // actually downloaded, so we just always pass the `--sysroot` option,

cargo.env("RUSTC_VERBOSE", self.verbosity.to_string()); if source_type == SourceType::InTree { <ins> let mut lint_flags = Vec::new(); </ins> // When extending this list, add the new lints to the RUSTFLAGS of the // build_bootstrap function of src/bootstrap/bootstrap.py as well as // some code doesn't go through this `rustc` wrapper. <del> rustflags.arg("-Wrust_2018_idioms"); rustflags.arg("-Wunused_lifetimes"); </del> <ins> lint_flags.push("-Wrust_2018_idioms"); lint_flags.push("-Wunused_lifetimes"); </ins> if self.config.deny_warnings { <del> rustflags.arg("-Dwarnings"); </del> <ins> lint_flags.push("-Dwarnings"); </ins> } // FIXME(#58633) hide "unused attribute" errors in incremental // builds of the standard library, as the underlying checks are // not yet properly integrated with incremental recompilation. if mode == Mode::Std && compiler.stage == 0 && self.config.incremental { <del> rustflags.arg("-Aunused-attributes"); </del> <ins> lint_flags.push("-Aunused-attributes"); </ins> } <ins> // This does not use RUSTFLAGS due to caching issues with Cargo. // Clippy is treated as an "in tree" tool, but shares the same // cache as other "submodule" tools. With these options set in // RUSTFLAGS, that causes *every* shared dependency to be rebuilt. // By injecting this into the rustc wrapper, this circumvents // Cargo's fingerprint detection. This is fine because lint flags // are always ignored in dependencies. Eventually this should be // fixed via better support from Cargo. cargo.env("RUSTC_LINT_FLAGS", lint_flags.join(" ")); </ins> } if let Mode::Rustc | Mode::Codegen = mode {