Same dependency has four different debug fingerprints

I have a Rust workspace at GitHub - deltachat/deltachat-core-rust: Delta Chat Rust Core library, used by Android/iOS/desktop apps, bindings and bots
Checked out tag v1.121.0, commit a8551510cdb5be7a35ebd3574df79d15a4fa471a

I am building the same workspace with various commands and observe the size of the target/ folder and the number of fingerprints for the aho-corasick dependency which is a transitive dependency of the regex crate.

I am using Rust 1.72 installed from the Arch Linux package rust without rustup. RUSTFLAGS variable is set to use mold: RUSTFLAGS=-C linker=clang -C link-arg=-fuse-ld=/usr/bin/mold.

  1. Remove target/ dir with rm -fr target.
  2. Run cargo test. The test finishes, at this point target/ dir is 2.7G in size. There is a single build of aho-corasick:
$ for f in target/debug/.fingerprint/aho-corasick-*/lib-aho_corasick.json; do cat $f; echo; done
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":2477126214476020920,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11093490409464552011]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-37295c684f6d8731/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
  1. Run cargo build. target/ dir is now 3.6G. There are two fingerprints for aho-corasick:
$ for f in target/debug/.fingerprint/aho-corasick-*/lib-aho_corasick.json; do cat $f; echo; done
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":2477126214476020920,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11093490409464552011]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-37295c684f6d8731/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":1802155187536016546,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,12497508451996929950]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-59155b355c38f51a/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
  1. Run cargo check. target/ dir is now 3.9G. aho-corasick has 3 fingerprints:
$ for f in target/debug/.fingerprint/aho-corasick-*/lib-aho_corasick.json; do cat $f; echo; done
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":2477126214476020920,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11093490409464552011]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-37295c684f6d8731/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":1802155187536016546,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,12497508451996929950]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-59155b355c38f51a/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":10570948055378307092,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,6704056115335731527]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-d4fc41e9be6f1168/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
  1. Run cargo clippy. target/ dir is now 4.0G. There are still 3 fingerprints for aho-corasick:
$ for f in target/debug/.fingerprint/aho-corasick-*/lib-aho_corasick.json; do cat $f; echo; done
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":2477126214476020920,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11093490409464552011]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-37295c684f6d8731/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":1802155187536016546,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,12497508451996929950]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-59155b355c38f51a/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":10570948055378307092,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,6704056115335731527]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-d4fc41e9be6f1168/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
  1. Run cargo check --workspace. target/ dir is now 4.5G. There are still 3 fingerprints for aho-corasick:
$ for f in target/debug/.fingerprint/aho-corasick-*/lib-aho_corasick.json; do cat $f; echo; done
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":2477126214476020920,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11093490409464552011]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-37295c684f6d8731/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":1802155187536016546,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,12497508451996929950]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-59155b355c38f51a/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":10570948055378307092,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,6704056115335731527]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-d4fc41e9be6f1168/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
  1. Run cargo check --workspace --all-targets. This processes benchmarks as well that were ignored up to this point. target/ dir is now 5.1G. There are now 4 fingerprints for aho-corasick:
$ for f in target/debug/.fingerprint/aho-corasick-*/lib-aho_corasick.json; do cat $f; echo; done
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":7825666741280757929,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11611369910886740696]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-029edd760a784a0c/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":2477126214476020920,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,11093490409464552011]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-37295c684f6d8731/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":1802155187536016546,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,12497508451996929950]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-59155b355c38f51a/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}
{"rustc":17224161603147387896,"features":"[\"default\", \"perf-literal\", \"std\"]","target":12812136000324506373,"profile":10570948055378307092,"path":6634768422494688907,"deps":[[6893260508610722743,"memchr",false,6704056115335731527]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-d4fc41e9be6f1168/dep-lib-aho_corasick"}}],"rustflags":["-C","linker=clang","-C","link-arg=-fuse-ld=/usr/bin/mold"],"metadata":13904389431191498124,"config":2202906307356721367,"compile_kind":0}

Note that I have not even made any --release builds, all the builds above have the same "profile root" (debug). Yet there are four fingerprints for the same dependency with the same features enabled. The difference is in the "profile" field and "deps" field.

Why the "profile" is different? Is it a hash of the profile structure? How to check which fields differ?

What is the last number in the "deps" array item? Why does it differ? I guess it is the firgerprint of dependency itself from the DepFingerprint structure.

I want to avoid building the same dependency four times in the debug build consuming disk space and compilation time. It seems completely unnecessary to rebuild it more than one time even when switching between the debug build and release build, after all this is not done for libc and standard library, so why should it be done for basic dependencies? Workspace even has optimizations for all dependencies outside workspace enabled as suggested in the Cargo Book:

[profile.dev.package."*"]
opt-level = "z"
1 Like

According to cargo::core::compiler::fingerprint - Rust CompileMode goes into the fingerprint. But why does the basic dependency should be built differently for check, test, build and bench mode? Is it possible to build the dependency once, say, in the build mode, and use the same built object files (packaged in .rlib archive) for debug builds, tests and benchmarks of my crate?

In target/debug/deps there are two rlib files and 4 metadata files related to aho-corasick:

-rw-r--r-- 1 user user  8.7K 2023-09-08 02:27 aho_corasick-029edd760a784a0c.d
-rw-r--r-- 1 user user   12K 2023-09-08 01:58 aho_corasick-37295c684f6d8731.d
-rw-r--r-- 1 user user   12K 2023-09-08 02:11 aho_corasick-59155b355c38f51a.d
-rw-r--r-- 1 user user  8.7K 2023-09-08 02:19 aho_corasick-d4fc41e9be6f1168.d
-rw-r--r-- 1 user user  1.6M 2023-09-08 02:27 libaho_corasick-029edd760a784a0c.rmeta
-rw-r--r-- 1 user user  3.1M 2023-09-08 01:58 libaho_corasick-37295c684f6d8731.rlib
-rw-r--r-- 1 user user  2.0M 2023-09-08 01:58 libaho_corasick-37295c684f6d8731.rmeta
-rw-r--r-- 1 user user  3.0M 2023-09-08 02:12 libaho_corasick-59155b355c38f51a.rlib
-rw-r--r-- 1 user user  2.0M 2023-09-08 02:12 libaho_corasick-59155b355c38f51a.rmeta
-rw-r--r-- 1 user user  1.6M 2023-09-08 02:19 libaho_corasick-d4fc41e9be6f1168.rmeta

So the dependency was actually compiled two times.

Found an issue about reusing .rmeta between cargo build and cargo check, building which for 500+ dependencies is also not free:

I have removed the target/ dir and ran only cargo build, it generated two .rlib files again:

-rw-r--r-- 1 user user   12K 2023-09-08 03:14 aho_corasick-37295c684f6d8731.d
-rw-r--r-- 1 user user   12K 2023-09-08 03:12 aho_corasick-59155b355c38f51a.d
-rw-r--r-- 1 user user  3.1M 2023-09-08 03:14 libaho_corasick-37295c684f6d8731.rlib
-rw-r--r-- 1 user user  2.0M 2023-09-08 03:14 libaho_corasick-37295c684f6d8731.rmeta
-rw-r--r-- 1 user user  3.0M 2023-09-08 03:13 libaho_corasick-59155b355c38f51a.rlib
-rw-r--r-- 1 user user  2.0M 2023-09-08 03:12 libaho_corasick-59155b355c38f51a.rmeta

There is only one entry for aho-corasick (version 1.0.2) in Cargo.lock.

I also noticed that aho-corasick builds only on .rlib on the master branch (commit 4a0585404a0a5f2e3edb4257a69d63efeb199ef3) currently.

The difference between stable and master is that on stable aho-corasick is a part of [build-dependencies].

Output of cargo tree -i aho-corasick on stable:

aho-corasick v1.0.2
โ””โ”€โ”€ regex v1.8.4
    โ”œโ”€โ”€ criterion v0.5.1
    โ”‚   [dev-dependencies]
    โ”‚   โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ”œโ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ”œโ”€โ”€ encoded-words v0.2.0 (https://github.com/async-email/encoded-words?branch=master#d55366b3)
    โ”‚   โ”œโ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ”‚   โ””โ”€โ”€ email v0.0.21 (https://github.com/deltachat/rust-email?branch=master#25702df9)
    โ”‚       โ”œโ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ”‚       โ””โ”€โ”€ lettre_email v0.9.2 (https://github.com/deltachat/lettre?branch=master#2ffdb534)
    โ”‚           โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ”œโ”€โ”€ env_logger v0.10.0
    โ”‚   โ””โ”€โ”€ pretty_env_logger v0.5.0
    โ”‚       [dev-dependencies]
    โ”‚       โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ”œโ”€โ”€ lettre_email v0.9.2 (https://github.com/deltachat/lettre?branch=master#2ffdb534) (*)
    โ”œโ”€โ”€ sanitize-filename v0.4.0
    โ”‚   โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    โ””โ”€โ”€ tracing-subscriber v0.3.17
        โ””โ”€โ”€ iroh v0.4.1
            โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
    [build-dependencies]
    โ””โ”€โ”€ unicode-linebreak v0.1.4
        โ””โ”€โ”€ textwrap v0.16.0
            โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)

Output on master:

aho-corasick v1.0.2
โ”œโ”€โ”€ regex v1.9.5
โ”‚   โ”œโ”€โ”€ criterion v0.5.1
โ”‚   โ”‚   [dev-dependencies]
โ”‚   โ”‚   โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ”œโ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ”œโ”€โ”€ encoded-words v0.2.0 (https://github.com/async-email/encoded-words?branch=master#d55366b3)
โ”‚   โ”‚   โ”œโ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ”‚   โ””โ”€โ”€ email v0.0.21 (https://github.com/deltachat/rust-email?branch=master#25702df9)
โ”‚   โ”‚       โ”œโ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ”‚       โ””โ”€โ”€ lettre_email v0.9.2 (https://github.com/deltachat/lettre?branch=master#2ffdb534)
โ”‚   โ”‚           โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ”œโ”€โ”€ env_logger v0.10.0
โ”‚   โ”‚   โ””โ”€โ”€ pretty_env_logger v0.5.0
โ”‚   โ”‚       [dev-dependencies]
โ”‚   โ”‚       โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ”œโ”€โ”€ lettre_email v0.9.2 (https://github.com/deltachat/lettre?branch=master#2ffdb534) (*)
โ”‚   โ”œโ”€โ”€ sanitize-filename v0.5.0
โ”‚   โ”‚   โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ”‚   โ””โ”€โ”€ tracing-subscriber v0.3.17
โ”‚       โ””โ”€โ”€ iroh v0.4.1
โ”‚           โ””โ”€โ”€ deltachat v1.121.0 (/home/user/src/deltachat/deltachat-core-rust)
โ””โ”€โ”€ regex-automata v0.3.8
    โ””โ”€โ”€ regex v1.9.5 (*)

So on stable output of cargo build -v indeed contains the line Running rustc --crate-name aho_corasick ...twice. One visible difference is that one build is with-C panic=abort(as specified inprofile.devsecton ofCargo.toml`) and another is without.

So if some crate has specified exactly the same dependency as you already have as [build-dependencies], it gets compiled another time. I now wonder if I can unify build flags for build-dependencies and dependencies so at least no non-duplicate crates are built twice with a single cargo build.

Cargo book says that build dependencies are compiled differently "to compile quickly" but in fact it results in compilation of the same dependency I already have twice. And trying to add a section [profile.dev.build-override] with panic = 'abort' results in error:

error: failed to parse manifest at `/home/user/src/deltachat/deltachat-core-rust/Cargo.toml`

Caused by:
  `panic` may not be specified in a `build-override` profile

I filed a bugreport to the cargo repo: build-override results in the same dependency being compiled twice ยท Issue #12645 ยท rust-lang/cargo ยท GitHub

We do if they have the same profile I believe. It is just that we disable all optimizations and debug info in the profile used for build dependencies to make them compile faster. When a crate is used as both build dep and regular dep this is requires compiling twice, but if it is only used as build dependency it compiles faster. On average this saves more than it costs in duplicate compilations.

1 Like

Can't cargo detect that build dependency is used as a normal dependency and not apply build-override? This may slow down the whole build in the corner case when cargo has nothing else to do because all further compilation depends on the the build dependency which cannot be compiled with all the CPUs and some CPUs become idle, but will definitely reduce the disk space requirements.

This is probably a minor issue though compared to the metadata reuse Consider allowing reuse of metadata between `cargo check` and `cargo build` ยท Issue #3501 ยท rust-lang/cargo ยท GitHub as only a small fraction of dependencies are build dependencies, but may be easier to fix.

That would actually require rebuilding the build dependency and all dependent crates if you add it as regular dependency.

Currently if I have some dependency only as a build dependency, and then add it as regular dependency, it has to be rebuilt without build-override too. So this use case does not get worse with my proposal.

Currently only that dependency needs to be built a second time, but all crates that depend on the original build dependency can keep using the original build dep without having to be rebuilt. With your proposal those all need to be rebuilt to use the version that is compiled with the non-build profile.

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.