How cargo works with dependencies or may it is bug?

cargo tree -d --format "{p} {f}" for my project shows this:

memchr v2.6.4 alloc,default,std
├── async-compression v0.3.15 flate2,gzip,tokio
│   └── reqwest v0.11.13...
...
memchr v2.6.4 alloc,std
└── nom v7.1.1 alloc,std
    ├── cexpr v0.6.0 
    │   └── bindgen v0.64.0 runtime
    │       [build-dependencies]
...
    └── nmea v0.2.0 default,std

And I can not understand what is going on.

At first, I thought that this is ok. cargo treats build time dependencies and runtime dependencies as different things. So it can build memchr twice, and not normalise dependencies as memchr v2.6.4 alloc,default,std.

But nmea is runtime dependency. So cargo would build and link the same version of memchr with different features twice to my application?

Or this is bug in cargo tree -d ?

Cargo will compile the same library two times if it is used both as a runtime and build-time dependency. Cargo will never link together two different versions of the same library into the final binary (unless their version numbers are not semver compatible).

4 Likes

So it is bug in cargo tree -d, which show that
my main crate depend on reqwest -> async-compression -> memchr v2.6.4 alloc,default,std and nmea -> nom -> memchr v2.6.4 alloc,std, where nmea and reqwest both are runtime dependecies?

I think cargo tree output is technically correct, it's just presented in an unclear way.

the copy of memchr is for bindgen which runs at build time, so gets a separate debug build of memchr and all other dependencies. Cargo isn't trying to reuse optimized runtime dependencies for unoptimized build scripts.

1 Like

bindgen yes it is build time dependency, but nmea which also depend on memchr without default feature on, is runtime dependency, not build time.

But cargo tree join bindgen and nmea into one subtree.

Simple Cargo.toml to reproduce problem:

[package]
name = "foo"
version = "0.1.0"
edition = "2021"

[dependencies]
async-compression = "=0.3.15"
nmea = "=0.2.0"

[build-dependencies]
bindgen = { version = "0.64.0", default-features = false, features = ["runtime"] }

cargo tree -d output:

❯ cargo tree -d
memchr v2.7.2
└── async-compression v0.3.15
    └── foo v0.1.0 (/private/tmp/foo)

memchr v2.7.2
└── nom v7.1.3
    ├── cexpr v0.6.0
    │   └── bindgen v0.64.0
    │       [build-dependencies]
    │       └── foo v0.1.0 (/private/tmp/foo)
    └── nmea v0.2.0
        └── foo v0.1.0 (/private/tmp/foo)

Looks like cargo tree and cargo check/build/test uses different algorithm to calculate dependency graph :frowning: So this makes cargo tree partly useless:

3 Likes

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.