Resolve dependencies based on feasible target/target_os platform combinations
Problem
There are a few related issues to this: #5896 is maybe the closest.
Concrete example:
I want to use the webbrowser crate.
webbrowser depends on jni for target_os = android
https://github.com/amodm/webbrowser-rs/blob/eff4ccbfd373e88df5dbfd6337eabd69b60b08be/Cargo.toml#L34-L35
The latest released jni depends on windows-sys for target = windows
https://github.com/jni-rs/jni-rs/blob/c79df460a2ca06984e35f67be8ddc13dfab542a7/Cargo.toml#L37-L38
Independent of build target this version of windows-sys will never be used.
Proposed Solution
Have the resolver figure out that windows-sys 0.45 will never be a dependency of webbrowser and therefore not of my project.
Notes
Now, I realize that one says target_os and the other is just the general "windows", so maybe it depends on that. But if the resolution could take these things into account (which I do not think it does, even if it using target_os = "windows", but correct me if I am wrong), that will help de-bloating Cargo.lock.
I can very much understand the need for this. There is also #7058 which asks for including the minimum set of dependencies needed for any supported set of --target values which is slightly different than #5896 which sounds like its focused on a specific invocation. Depending on the approach, that issue could be a superset of this issue. You can find a summary of our team discussion on that in https://blog.rust-lang.org/inside-rust/2025/02/27/this-development-cycle-in-cargo-1.86/#specifying-supported-platforms-in-packages
In those notes, it mentions that tracking the path to a dependency to know which cfgs are relevant can get quite tricky. On top of that, doing the cfg set logic between the different target tables could end up being very brittle and difficult for us to maintain the expectations that changing the version of cargo should not change your Cargo.lock.
This would help a lot to clean up the lockfiles, vendoring and SBOM, you have a lot of libraries now that support OS like redox and it is just annoying to have a security ping because some dependency that you don't use needs to be patched.
maintain the expectations that changing the version of cargo should not change your Cargo.lock
Is this really relevant if the new Cargo.lock is improved?
Then, naturally, I understand the hassle when running different cargo versions give different lock-files.
For me, I still want a multi-platform package, so I am fine with potentially supporting redox, just that packages that can never be in any valid combination is removed. So one will have to look at the sequence of dependencies rather than solving for a specific platform. Good thing is that those dependencies will never be used so the Cargo.lock will always be valid, independent of platform actually invoked on. Bad thing is that it is probably harder than #7058 and #5896.
I have no idea how the current dependency checking is done, but assuming that you have a directed graph of dependencies, where dependencies are nodes/leaves/branches and features are conditions on edges, one should be able to traverse from every leaf node and check if there is a valid path to the root, taking the conditions into consideration. If not, the leaf node is not required.
Maybe this is what is already done and the reason is the mix-up of target = windows and target_os = android? I guess that target = windows is not even valid (anymore?) as it is not a target triple?
Edit: it seems like target = windows should be interpreted as target = unknown-windows-unknown, so should be the same as target_os = windows.
We can evolve the lockfile by bumping the format version. The version is initially selected based on MSRV and is then sticky. The first big problem is if the lockfile is dependent on information that is versioned independently of the lockfile. The second is that we need to keep around each implementation to support writing old versions.
If nothing else, avoiding lockfile churn avoids churn or even broken vendor/ because we vendor what we lock.
Note that removing unreachable dependencies due to how cfgs interact is the most complex solution for when someone supports a limited set of targets, so restricting to that case doesn't make this any easier to get in and, most likely, just removing unreachable dependencies without supported-targets will offer the least end-user benefits.
Something this lacks is an "end user impact": why does this matter?
Would a solution where you explicitly enumerate your supported targets (a potential solution for #7058) work?
Otherwise, if this remains about automatically trimming dependencies based on cfg logic, this seems like this would be rejected by the cargo team for the reasons given in the previously linked blog post.
I'd say that this is similar to not pulling in transitive dependencies of disabled features in used dependencies (which I am quite convinced is the case, e.g., based on the discussions in #10801, although I couldn't really find a definitive answer).
End user impact? If the end user is the developer, it is the lock-file bloat that is the main reason for me. The lock file is typically large enough as it is to avoid filling it with dependencies that will never be used, independent of build platform and features. Another possible reason is license bloat. Say that the dependency that can never be used is using a license that we do not want to use (typically GPL). This will give an incorrect impression that my code may be under GPL because a dependency is. Although it is possible to "prove" that this is not the case, automatic license checking and documentation systems will have to be improved to detect it, as one could assume that everything in the lock-file can be used for some combination of features and platforms.
With that said, I realize that it may be low priority and possibly hard to solve for every possible cfg-value and combination. But at least target/target_os seems to be a reasonable cfg-value to start with that will deal with a bit.
I'd say that this is similar to not pulling in transitive dependencies of disabled features in used dependencies
We try to reduce them but it isn't complete, e.g. #10801. There are orders of magnitude difference in development and maintenance between what we do today, solving #10801, and solving this.
Say that the dependency that can never be used is using a license that we do not want to use (typically GPL). This will give an incorrect impression that my code may be under GPL because a dependency is.
The lockfile likely shouldn't be used for situations like this. You can manually inspect using cargo tree --format '{p} ({l})' (and whatever platform and feature flags you want). rust-lang/rfcs#3553 would help in knowing what went into your final product.
To repeat my question from earlier which seems to have not been addressed
Would a solution where you explicitly enumerate your supported targets (a potential solution for https://github.com/rust-lang/cargo/issues/7058) work?