I think cargo tree gives a wrong output for my project. I already saw a similar issue on the cargo github for a different project, where it was found that the output was actually correct. So I wanted to ask here if I am missing something before opening an issue.
cargo tree -e features shows an edge from parquet v56.2.0 to ring feature "std". But parquet should not depend on ring at all without the encryption feature.
Am I misunderstanding how optional dependencies work? Or am I misreading the cargo tree output?
Thank you for finding this. But I am still curious why the tree shows an edge from parquet v56.2.0 to ring feature "std". I would expect it to be from parquet feature "encryption" to ring feature "std", since parquet itself does not need ring, only the encryption feature does. Am I misunderstanding how the feature edge tree works?
I am asking this because in my actual project I am using the datafusion crate, which depends on many other datafusion-* crates, many of which in turn depend on parquet with various features required. So I wanted to find out which features on datafusion caused the ring crate to be included and I was hoping to find this chain of dependencies via cargo tree, rather than manually iterating over the different datafusion-* crates to find the cause.
I have ≈ 0 knowledge about workspaces as I still live in the comfy confines of plain packages. Can you explain what's happening using the original Cargo.toml? My (likely incorrect) understanding is that the parquet feature depends on the dependency parquet as defined in the workspace's Cargo.toml and the implied feature tempfile as defined in the workspace based on this:
parquet = { version = "56.2.0", default-features = false, features = [
"arrow",
"async",
"object_store",
] }
tempfile = "3"
I'm confused as why the version in the converted Cargo.toml you linked to has version 56.0.0 and not 56.2.0.
Addendum
I think I see the issue. The Git tag 50.1.0 does not line up with the version on crates.io. They seemed to have published 50.1.0 on crates.iobefore making the Git tag 50.1.0 which included the removal of the encryption feature. Tsk, tsk, tsk[1].
Misread the Git repo :(.
I know Git tags are distinct from crate versions; but seeing how the versions line up, I'm guessing the intent is for them to represent the same thing.↩︎
I too am not very familiar with how workspace dependencies work either. Here is my original Cargo.toml, though I don't think the extra dependencies are relevant:
datafusion = { default-features = false, features = [
"parquet",
], version = "50.0.0" }
tokio = { version = "1", default-features = false, features = ["fs"] }
futures = "0.3.31"
anyhow = "1"
fast-clock = "0.1.1"
Here again, you can see that the tree shows ring as a dependency of the parquet crate itself, not of its encryption feature (which it should be). The parquet package in turn is a dependency of datafusion-common. datafusion-common does however not depend on the encryption feature of parquet (unless its parquet_encryption feature is enabled). So the tree shows that ring is included as a transitive dependency of datafusion-common, which is not the case.
On a side note, I am looking up these dependencies on github, and based on release dates of the creat versions and modification dates of files on github, I am assuming that these are also in the versions released on crates.io. Is there a convenient way of getting the Cargo.toml files from the versions I am actually using?
I was wrong. I checked out the tag that lines up with the version on crates.io, and it has the encryption feature. There is technically nothing that requires them to match up; but if you're doing it "right", then they should. I'll let @kpreid answer the rest since I am unfamiliar with workspaces. I apologize for my mistake. I should have exercised more patience before responding.
The cargo tree output doesn’t actually include the feature relationships defined in the Cargo.toml. It only tells you what features are enabled by which dependent, not why that dependent enabled that feature. Entries will always look like:
foo feature "f1"
└── foo vX.Y.Z
└── bar feature "f2"
without containing the information that foo's Cargo.toml contains features.f1 = ["bar/f2"] (if it did). The foo vX.Y.Z entry of the tree will list all of its dependencies without listing why those dependencies exist, including showing dependencies that are not due to that occurrence of foo in the tree (that is actually representing a graph).
Oh, ok. That is a bit disappointing. Is there a way to get this feature dependency graph? I think that would be quite useful. Or is there a better way to find this kind of root cause?