`cargo tree -e features` gives wrong output?

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.

These are my dependencies:

datafusion-datasource= { version="50", default-features=false ,features=["parquet"]}
parquet = "56.2"

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?

Please post the complete cargo tree output, so we can study it without having to set up our own copy.

The full output is here. It is unfortunately too long to post.

Here is the inverted output (cargo tree -e features -i ring)

datafusion-datasource's Cargo.toml defines:

[dependencies.parquet]
default-features = false
features = ["arrow", "async", "object_store", "encryption"]
optional = true
version = "56.0.0"

This includes the "encryption" feature, and is unavoidable as long as you use datafusion-datasource = { features = ["parquet"] }.

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:

[features]
parquet = ["dep:parquet", "tempfile"]

[dependencies]
parquet = { workspace = true, optional = true }
tempfile = { workspace = true, optional = true }

The workspace's Cargo.toml has

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.io before making the Git tag 50.1.0 which included the removal of the encryption feature. Tsk, tsk, tsk[1].

Misread the Git repo :(.


  1. 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. ↩︎

The workspace Cargo.toml from the 50.x branch has the parquet/encryption feature enabled:

This was removed on the main branch in this pull request, which was merged but not yet published to crates.io: fix: Remove parquet encryption feature from root deps by Vyquos · Pull Request #17700 · apache/datafusion · GitHub

1 Like

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"

And here is the resulting tree

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?

I’m not aware of a tool that does that, but that doesn’t mean one hasn’t been written.

I could not find any such tool, so I created a feature request for cargo.