§ Part I: a little surprise
I'd spend a fair amount of time to setup EMACS for Rust development.
All of a sudden, I've recently realized I could not run
cargo build easily anymore, I often had this message:
$ cargo build Blocking waiting for file lock on build directory
and there I had to wait for long (like, a full minute!).
Ooook, let's spend some quality-time debugging *groan*
So, where do we start? The message, of course. Here I see someone is experiencing the same symptoms with another setup. The message lingo basically says there are concurrent tasks attempting to compile sources. Uhm ... and who is the other ghost compiling process?
I'll start doing some tests randomly saving a buffer in EMACS after or before a fresh build. After a while I see that simply opening a file in a buffer is triggering a chain reaction of
cargo test processes:
/home/me/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo test --no-run --bin my_rust_project --message-format=json /home/me/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo test --no-run --lib --message-format=json
What in the world is triggering a tests run!?
§ Part II: the facepalm
Still tests running when I open a file.
Damn, ok let's patiently comment all the Prelude modules and see which one is triggering this. Turns out that any modules I have enabled triggers this behaviour, so there must be a common module above all.
Fast-forward: the culprit is the
prelude-programming module, namely this piece of code:
(if (fboundp 'global-flycheck-mode) (global-flycheck-mode +1) (add-hook 'prog-mode-hook 'flycheck-mode))
What does Flycheck know about Rust? A syntax checker that triggers tests? *sigh*
So after some more research, I see that Flycheck does know Jack about Rust and in a way I didn't expect: a
flycheck-rust-check-tests config parameter.
(flycheck-def-option-var flycheck-rust-check-tests t (rust-cargo rust) "Whether to check test code in Rust. For the `rust' checker: When non-nil, `rustc' is passed the `--test' flag, which will check any code marked with the `#[cfg(test)]' attribute and any functions marked with `#[test]'. Otherwise, `rustc' is not passed `--test' and test code will not be checked. Skipping `--test' is necessary when using `#![no_std]', because compiling the test runner requires `std'. For the `rust-cargo' checker: When non-nil, calls `cargo test --no-run' instead of `cargo check'." :type 'boolean :safe #'booleanp :package-version '("flycheck" . "0.19"))
flycheck-rust-check-tests is set to
cargo is installed, flycheck will execute
cargo test --no-run instead of
cargo check. Let's do this and add a line to the Rust prelude module (
prelude-rust.el) trying to mute that parameter:
(setq flycheck-rust-check-tests nil)
and ... the
cargo test little devils are not spawned anymore.
§ Part III: the unanswered questions
The saying goes that if you reproduce a bug, you're halfway to its resolution. I'll add that fixing the bug takes you to a 90%; but only understanding the cause of a behaviour unlocks the real 100% achievement.
So what triggered such a
cargo test frenzy? Has it ever always been there, just unnoticed?
The most likely answer is that Flycheck run
cargo test and I manually run
cargo build from the command line. Each of these two commands invalidates the compiled cache, so what happened is something like this:
- Open file in buffer (
cargo testtriggered, slow run unnoticed)
- Save buffer (
cargo testtriggered: fast run)
- from CLI run
cargo buildto run my application (slow run)
- /me wtf?!
- Save buffer (
cargo testtriggered: slow run unnoticed)
- from CLI run
cargo build(message warning about concurrent build)
- /me wtf?! again
Running several times in a row only one of these two commands doesn't invalidate the build cache, so the issue doesn't happen.
nil that variable made Flycheck switch from
cargo test to
cargo check to get errors produced the following benefits:
cargo checkis the recommended way to get compilation warning/errors and in some scenario should speed things up
- informed me to not run
cargo buildunless I really need to
- does not run
cargo testto get syntax/lint errors, which was awkard and confusing in the first place
- BUG: there's an old outstanding bug, due to
cargo checkmetadata caching: on --lib cargo projects (not --bin) it only shows compiler warnings once after a rebuild, see issue
Flycheck command before:
cargo test --no-run --lib --message-format=json
cargo check --lib --message-format=json
Finally, let's ensure this won't happen again - let's save this in my custom EMACS config:
set the value to
nil, then save to
~/.emacs.d/personal/custom.el. This improve upon the previous solution as now I don't need to customize
Second step, disable
rust from the list of checkers:
custom.el has two new configs:
'(flycheck-disabled-checkers (quote (rust))) '(flycheck-rust-check-tests nil)
Closing thoughts: an outstanding issue in the Rust world is how to speed compiling times up, but that's nothing we can do here, eventually we will get there.
Oh, one last comment: I hate elisp so much that I find a perverse pleasure in understanding how it works.