Saving Recompilations with --release

I run my cargo test and cargo install both with --release, hoping to save recompiling scores of slow dependencies every time. However, it still unnecessarily recompiles most of them. Is there any way around this?

You mention cargo install. Are you installing binaries locally, or what's going on?

Yes, these are my cargo test and cargo install commands:

cargo test --release --  --color always --nocapture
cargo install -f --path . --root  $HOME

Ideally, I do not want the second command to be recompiling anything (after successful tests).

And I certainly do not want everything recompiled and duplicated in 'Debug', as it takes me less time to find the bug(s) than to recompile. Not to mention all those voluminous dependencies being duplicated as well.

I do not think you can avoid the recompile when using cargo install, but you can do a cargo build --release instead and use an ordinary copy command to replace your installed binary when testing.

2 Likes

cargo tests builds and runs tests (unit/integration/doctests), not necessarily your application. Perhaps that's why you see some rebuilding happening.

Deps only need to be built once unless there's a change in Cargo.lock file. Do consecutive cargo build --release runs rebuild deps repeatedly?

That's not true. Same dependencies can be used with different sets of features flags, so it's pretty normal for cargo to compile some of dependencies separately for tests and for main release binary if some features are only enabled for dev enrionment (or only required by dev dependencies).

cargo install compiles binaries in a fresh directory by default. From this issue, it seems that setting CARGO_TARGET_DIR should prevent that.

1 Like

Yes, that's true but I had the impression that you use the same features, based on your examples above.

Depending on how much you don't want to rebuild one option could be to run cargo test --release followed by cargo build --release.

I haven't used cargo install on my own projects but one way of reusing the same build directory is via docker bind mounts/volumes. We use something along the lines of "Compile your app inside the Docker container" in Docker to reuse as much as possible. Haven't used it with install though.

@alice @qaopm - I tried. Even with the same features, cargo test --release puts everything in release/deps and cargo build --release puts the same things in release/build, thus everything is compiled twice and kept twice and there is no synergy whatsoever. Not at all like good old makefiles.

I would not mind if the final executables were different, as they are, because of including/excluding the tests but for all their dependencies to be reduplicated seems totally unnecessary.

1 Like

Hm, I don't know anything about cargo/rustc internals, in my case both cargo build --release and cargo test --release populate both deps and build. If I do

cargo test --release

first, then the build is a no-operation:

$ cargo build --release
    Finished release [optimized] target(s) in 0.37s

Doing it the other way round (build first, then test) is similar, except that test has dev deps so it needs to build them. It doesn't build shared deps.

For local crates, cargo install and cargo build should use the same caches (unlike cargo test —release, which might actually use a different profile).

cargo test --release uses the bench profile.

If you want it to share artifacts with cargo build --release, then you must make sure that the bench profile and release profile have the exact same compilation options.

2 Likes

Oh boy, what a lot of complications with hidden profiles. I never suspected that. Thanks for the information!
I understood some fairly complex things in the past but I can not make any sense out of this. There seems to be no way of telling for sure where those 'artifacts' will end up when.

The profile used depends on the command, the package, the Cargo target, and command-line flags like --release .

That is a four dimensional defaults space in which I am lost.
Sometimes I can not help feeling that you Rust experts take pleasure in complicating things just for the sake of it.
Surely, the default should be that everything goes in one place and avoids slow voluminous compilations and only if you want lots of baroque 'profiles' in all kinds of different places, then you set them up. Not the other way round.

1 Like

Ah, they do, but it is not that simple:

Note that when using the cargo test and cargo bench commands, the test / bench profiles only apply to the final test executable. Dependencies will continue to use the dev / release profiles.

IMHO these default profiles are a total mess. I will just have to every time manually specify my own.

1 Like

I have finally succeeded in forcing cargo into doing something sensible by sticking all the following duplicated profiles into my Cargo.toml file and using --release with both cargo test and cargo build. However, cargo install still insists on recompiling, for reasons that escape me. I will just have to use cargo build --release instead.
On the plus side, my executable has mysteriously lost one megabyte in the process.

[profile.test]
opt-level = 3
debug = false
debug-assertions = false
overflow-checks = false
lto = true
incremental = true
rpath = false

[profile.release]
opt-level = 3
debug = false
debug-assertions = false
overflow-checks = false
lto = true
panic = 'unwind'
incremental = true
rpath = false

[profile.bench]
opt-level = 3
debug = false
debug-assertions = false
overflow-checks = false
lto = true
incremental = true
rpath = false

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.