Skip to content

Inconsistent feature resolution with artifact dependencies (bindeps) depending on whether or not the target key is specified #11547

Open
@bstrie

Description

@bstrie

The issue here can be reproduced by the following repo: https://github.com/bstrie/bindeperror6 (you will need to manually comment out one of the two duplicate bindep lines in Cargo.toml, which demonstrate different behavior).

Assume the following conditions:

  1. You have a top-level crate that specifies a binary artifact dependency.
  2. Both the top-level crate and and the bindep have a shared dependency on a third crate.
  3. The top-level crate and the bindep specify different crate features for their shared dependency.

There are two questions to answer here:

  1. Does Cargo currently unify the feature profiles for the shared crate? If so, under what conditions?
  2. Should Cargo ever unify feature profiles across artifact dependencies?

Let's continue using the repo linked above.

With the line mybindep = { path = "mybindep", artifact = "bin" } in Cargo.toml, we can run cargo clean && cargo build -v and observe only a single invocation of rustc, which features the flag --cfg 'feature="XXX"', demonstrating that features have been merged in the shared dependency.

However, with the line mybindep = { path = "mybindep", artifact = "bin", target = "x86_64-unknown-linux-gnu" } (note the explicit target key), we can run cargo clean && cargo build -v to observe that rustc is invoked twice, where the only difference between the invocation is --cfg 'feature="XXX"' (as well as a different hash value, to uniquely identify the compiled artifacts). This produces two versions of the shared dependency with different feature profiles. This would be ordinary if the host/default target were different from the target listed for the bindep, but in this case both targets are x86_64-unknown-linux-gnu.

We can show that this is due to the presence of the target key in Cargo.toml, rather than due to an edge case of the listed target being equal to the host target. With the line mybindep = { path = "mybindep", artifact = "bin", target = "x86_64-unknown-linux-musl" } (note that the target is now musl), we can run cargo clean && cargo build -v --target x86_64-unknown-linux-musl to show that the shared dependency is once again compiled twice, with two distinct feature sets.

Intuitively, it seems like users should be able to assume that the behavior here should depend on the targets that are ultimately selected, rather than presence or absence of the target key itself. Thus, this is a bug regardless of which behavior is correct.

As for resolving this inconsistency, we need to determine which behavior is actually desired. On the one hand, aggressively unifying features can reduce compilation times and disk usage. On the other hand, it was an over-aggressive approach to feature unification that led to the introduction of the 2.0 feature resolver, which deliberately reduces the amount of unification that is done between artifacts of different categories (dev-dependencies, build-dependencies, etc.), even at the potential expense of build times. We can argue that artifact dependencies are just such a "different category" of artifact, which should be allowed to have independent, non-unified feature profiles. The advantages of this are: it makes the bindeps feature a drop-in replacement for the current stable workaround (shelling out to Cargo in a build script); it potentially reduces the binary size of artifact dependencies by eliminating features that are entirely unused; and it allows users reduce the number of transitive dependencies (via optional dependencies) relied upon by an artifact dependency, thus decreasing its trusted computing base, which matters in a world where awareness of supply chain attacks is rapidly increasing. (This last point is of particular concern for our use case specifically, since our bindeps are run in a more secure context than the main crate.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: bugS-needs-mentorStatus: Issue or feature is accepted, but needs a team member to commit to helping and reviewing.Z-bindepsNightly: binary artifact dependencies

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions