-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Rustc cache #5359
Rustc cache #5359
Conversation
We want to create `rustc` only when we already have workspace, so that we could use the `target` dir to store cached info about the compiler.
(rust_highfive has picked a reviewer for you, use r? to override) |
src/cargo/util/rustc.rs
Outdated
paths::mtime(&rustup_rustc)?.hash(&mut hasher); | ||
} | ||
_ => (), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This match requires some scrutiny during review, because it is not tested, but kinda important :-)
We can't just look at RUSTUP_TOOLCHAIN
, because the toolchain name does not change when you do rustup update
, it is just stable-x86_64-unknown-linux-gnu
. So we actually have to look inside the toolchain and mtime the real rustc binary.
This still has a failure mode unfortunately :( If the cargo
is not rustup, but rustc
is rustup, then these env variables won't be set, and we won't be able to detect compieler changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One possible mitigation here I think would be to look at the result of the resolved executable. If rustc
resolves to $CARGO_HOME/.bin/rustc
then I think we can say with a high level of certainty that it's a rustup rustc. We could even say that if we detect a rustup rustc and RUSTUP_HOME
and/or RUSTUP_TOOLCHAIN
isn't set that we skip this cache entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, implemented that logic!
@bors try |
Rustc cache This implements rustc caching, to speed-up no-op builds. The cache is per-project, and stored in `target` directory. To implement this, I had to move `rustc` from `Config` down to `BuildConfig`. closes #5315
💔 Test failed - status-appveyor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite get what the earlier refactorings are trying to achieve. However, I highly appreciate that we should probably cache the output of the rustc invocation. Can't we just hide the cache inside Config::rustc()
. Also, should we (first? I don't know) try to combine the calls to rustc for getting the version and the TargetInfo
?
src/cargo/util/config.rs
Outdated
self.maybe_get_tool("rustc_wrapper")?, | ||
) | ||
}) | ||
pub fn new_rustc(&self) -> CargoResult<Rustc> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why rename this here? Is that just cosmetic? Maybe extract it into a separate commit that explains why this makes sense on its own?
if let Ok(rustc) = cx.config.rustc() { | ||
rustc.verbose_version.hash(&mut hasher); | ||
} | ||
cx.build_config.rustc.verbose_version.hash(&mut hasher); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my perception, it seemed that build_config
should probably stay immutable over the lifetime of the Context
(and in fact that is enforced, I think, by #5358).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, not sure I understand this comment :) build_config
is not mutated here at all?
I’ve initially tried to just add caching to Config.rustc, but ended up moving rustc to BuildConfig first. The reason is that we need to store the cache somewhere, and the logical choice is to store it in the target directory of the project. However, Config does not know the target directory, because it does not know the current project. |
Yeah, that part actually makes sense to me -- I had been thinking about doing the same. However, it seems you've made the mechanics of doing so more complicated than what seems necessary. I guess you had some other goals at the same time, or maybe this |
@bors try |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking fantastic to me, thanks so much for doing this @matklad!
One final aspect I'd want to consider as well is the rustbuild integration aspect. I think though that all the logic here is sufficient to detect changes in that situation, and we can always set the env var if need be to disable this caching entirely.
src/cargo/util/paths.rs
Outdated
@@ -129,6 +158,12 @@ pub fn append(path: &Path, contents: &[u8]) -> CargoResult<()> { | |||
Ok(()) | |||
} | |||
|
|||
pub fn mtime(path: &Path) -> CargoResult<FileTime> { | |||
let meta = | |||
fs::metadata(path).chain_err(|| internal(format!("failed to stat `{}`", path.display())))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general internal
hasn't worked out great for Cargo and I've been trying to remove it wherever I see it, so while you're here mind removing this internal
?
|
||
/// It is a well known that `rustc` is not the fastest compiler in the world. | ||
/// What is less known is that even `rustc --version --verbose` takes about a | ||
/// hundred milliseconds! Because we need compiler version info even for no-op |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this cc the Cargo/rustc issues as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/cargo/util/rustc.rs
Outdated
|
||
impl Drop for Cache { | ||
fn drop(&mut self) { | ||
match (&self.cache_location, self.dirty) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps this could be started with:
if !self.dirty {
return
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er well I'll leave it up to you, I'm just personally always looking to aggressively reduce indent!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it looks much nicer that way, because I don't have to wrangle with not moving out cahce_location as well!
src/cargo/util/rustc.rs
Outdated
|
||
paths::mtime(&path)?.hash(&mut hasher); | ||
|
||
match (env::var("RUSTUP_HOME"), env::var("RUSTUP_TOOLCHAIN")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you insert a comment here as to what's going on?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
src/cargo/util/rustc.rs
Outdated
paths::mtime(&rustup_rustc)?.hash(&mut hasher); | ||
} | ||
_ => (), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One possible mitigation here I think would be to look at the result of the resolved executable. If rustc
resolves to $CARGO_HOME/.bin/rustc
then I think we can say with a high level of certainty that it's a rustup rustc. We could even say that if we detect a rustup rustc and RUSTUP_HOME
and/or RUSTUP_TOOLCHAIN
isn't set that we skip this cache entirely.
src/cargo/ops/cargo_compile.rs
Outdated
@@ -260,7 +260,8 @@ pub fn compile_ws<'a>( | |||
bail!("jobs must be at least 1") | |||
} | |||
|
|||
let mut build_config = BuildConfig::new(config, jobs, &target)?; | |||
let rustc_info_cache = ws.target_dir().join("rustc_info.json").into_path_unlocked(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind throwing a .
on the front to hide it away?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✔️
Quite probably, but, if I did this, it is because of being stupid, and not because of being smart :-) What simplifications would you suggest? |
@bors: r+ |
📌 Commit 76b0a8a has been approved by |
Rustc cache This implements rustc caching, to speed-up no-op builds. The cache is per-project, and stored in `target` directory. To implement this, I had to move `rustc` from `Config` down to `BuildConfig`. closes #5315
For posterity, a couple of more details:
|
☀️ Test successful - status-appveyor, status-travis |
Let's tag it relnotes, because some folks might need to be aware of |
I think this was left behind as part of rust-lang#5359.
Remove some minor, unused code. I think this was left behind as part of #5359.
Coming in here late but I ran into this issue while reading old release notes. Note that sccache only uses the hash of the sysroot libs as input to the hash function for compilation caching--it doesn't recalculate that hash every time (that'd be expensive!) it caches that hash internally with the same strategy you're using here (by comparing mtimes). sccache doesn't have the checks for rustup you added, we've got an open issue on handling rustup properly. |
This implements rustc caching, to speed-up no-op builds.
The cache is per-project, and stored in
target
directory. To implement this, I had to moverustc
fromConfig
down toBuildConfig
.closes #5315