Skip to content

Commit d6dd1bf

Browse files
authored
Merge pull request #259 from ehuss/cargo-resolver-migration
Add details on migrating the Cargo feature resolver.
2 parents 754f910 + 3a23749 commit d6dd1bf

File tree

1 file changed

+152
-1
lines changed

1 file changed

+152
-1
lines changed

src/rust-2021/default-cargo-resolver.md

+152-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,156 @@ The new feature resolver no longer merges all requested features for
1616
crates that are depended on in multiple ways.
1717
See [the announcement of Rust 1.51][5] for details.
1818

19-
[4]: https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2
19+
[4]: ../../cargo/reference/resolver.html#feature-resolver-version-2
2020
[5]: https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#cargos-new-feature-resolver
21+
22+
## Migration
23+
24+
There are no automated migration tools for updating for the new resolver.
25+
For most projects, there are usually few or no changes as a result of updating.
26+
27+
When updating with `cargo fix --edition`, Cargo will display a report if the new resolver will build dependencies with different features.
28+
It may look something like this:
29+
30+
> note: Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo.
31+
> This may cause some dependencies to be built with fewer features enabled than previously.
32+
> More information about the resolver changes may be found at <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html><br>
33+
> When building the following dependencies, the given features will no longer be used:
34+
>
35+
> ```text
36+
> bstr v0.2.16: default, lazy_static, regex-automata, unicode
37+
> libz-sys v1.1.3 (as host dependency): libc
38+
> ```
39+
40+
This lets you know that certain dependencies will no longer be built with the given features.
41+
42+
### Build failures
43+
44+
There may be some circumstances where your project may not build correctly after the change.
45+
If a dependency declaration in one package assumes that certain features are enabled in another, and those features are now disabled, it may fail to compile.
46+
47+
For example, let's say we have a dependency like this:
48+
49+
```toml
50+
# Cargo.toml
51+
52+
[dependencies]
53+
bstr = { version = "0.2.16", default-features = false }
54+
# ...
55+
```
56+
57+
And somewhere in our dependency tree, another package has this:
58+
59+
```toml
60+
# Another package's Cargo.toml
61+
62+
[build-dependencies]
63+
bstr = "0.2.16"
64+
```
65+
66+
In our package, we've been using the [`words_with_breaks`](https://docs.rs/bstr/0.2.16/bstr/trait.ByteSlice.html#method.words_with_breaks) method from `bstr`, which requires `bstr`'s "unicode" feature to be enabled.
67+
This has historically worked because Cargo unified the features of `bstr` between the two packages.
68+
However, after updating to Rust 2021, the new resolver will build `bstr` twice, once with the default features (as a build dependency), and once with no features (as our normal dependency).
69+
Since `bstr` is now being built without the "unicode" feature, the `words_with_breaks` method doesn't exist, and the build will fail with an error that the method is missing.
70+
71+
The solution here is to ensure that the dependency is declared with the features you are actually using.
72+
For example:
73+
74+
```toml
75+
[dependencies]
76+
bstr = { version = "0.2.16", default-features = false, features = ["unicode"] }
77+
```
78+
79+
In some cases, this may be a problem with a third-party dependency that you don't have direct control over.
80+
You can consider submitting a patch to that project to try to declare the correct set of features for the problematic dependency.
81+
Alternatively, you can add features to any dependency from within your own `Cargo.toml` file.
82+
For example, if the `bstr` example given above was declared in some third-party dependency, you can just copy the correct dependency declaration into your own project.
83+
The features will be unified, as long as they match the unification rules of the new resolver. Those are:
84+
85+
* Features enabled on platform-specific dependencies for targets not currently being built are ignored.
86+
* Build-dependencies and proc-macros do not share features with normal dependencies.
87+
* Dev-dependencies do not activate features unless building a target that needs them (like tests or examples).
88+
89+
A real-world example is using [`diesel`](https://crates.io/crates/diesel) and [`diesel_migrations`](https://crates.io/crates/diesel_migrations).
90+
These packages provide database support, and the database is selected using a feature, like this:
91+
92+
```toml
93+
[dependencies]
94+
diesel = { version = "1.4.7", features = ["postgres"] }
95+
diesel_migrations = "1.4.0"
96+
```
97+
98+
The problem is that `diesel_migrations` has an internal proc-macro which itself depends on `diesel`, and the proc-macro assumes its own copy of `diesel` has the same features enabled as the rest of the dependency graph.
99+
After updating to the new resolver, it fails to build because now there are two copies of `diesel`, and the one built for the proc-macro is missing the "postgres" feature.
100+
101+
A solution here is to add `diesel` as a build-dependency with the required features, for example:
102+
103+
```toml
104+
[build-dependencies]
105+
diesel = { version = "1.4.7", features = ["postgres"] }
106+
```
107+
108+
This causes Cargo to add "postgres" as a feature for host dependencies (proc-macros and build-dependencies).
109+
Now, the `diesel_migrations` proc-macro will get the "postgres" feature enabled, and it will build correctly.
110+
111+
The 2.0 release of `diesel` (currently in development) does not have this problem as it has been restructured to not have this dependency requirement.
112+
113+
### Exploring features
114+
115+
The [`cargo tree`] command has had substantial improvements to help with the migration to the new resolver.
116+
`cargo tree` can be used to explore the dependency graph, and to see which features are being enabled, and importantly *why* they are being enabled.
117+
118+
One option is to use the `--duplicates` flag (`-d` for short), which will tell you when a package is being built multiple times.
119+
Taking the `bstr` example from earlier, we might see:
120+
121+
```console
122+
> cargo tree -d
123+
bstr v0.2.16
124+
└── foo v0.1.0 (/MyProjects/foo)
125+
126+
bstr v0.2.16
127+
[build-dependencies]
128+
└── bar v0.1.0
129+
└── foo v0.1.0 (/MyProjects/foo)
130+
131+
```
132+
133+
This output tells us that `bstr` is built twice, and shows the chain of dependencies that led to its inclusion in both cases.
134+
135+
You can print which features each package is using with the `-f` flag, like this:
136+
137+
```console
138+
cargo tree -f '{p} {f}'
139+
```
140+
141+
This tells Cargo to change the "format" of the output, where it will print both the package and the enabled features.
142+
143+
You can also use the `-e` flag to tell it which "edges" to display.
144+
For example, `cargo tree -e features` will show in-between each dependency which features are being added by each dependency.
145+
This option becomes more useful with the `-i` flag which can be used to "invert" the tree.
146+
This allows you to see how features *flow* into a given dependency.
147+
For example, let's say the dependency graph is large, and we're not quite sure who is depending on `bstr`, the following command will show that:
148+
149+
```console
150+
> cargo tree -e features -i bstr
151+
bstr v0.2.16
152+
├── bstr feature "default"
153+
│ [build-dependencies]
154+
│ └── bar v0.1.0
155+
│ └── bar feature "default"
156+
│ └── foo v0.1.0 (/MyProjects/foo)
157+
├── bstr feature "lazy_static"
158+
│ └── bstr feature "unicode"
159+
│ └── bstr feature "default" (*)
160+
├── bstr feature "regex-automata"
161+
│ └── bstr feature "unicode" (*)
162+
├── bstr feature "std"
163+
│ └── bstr feature "default" (*)
164+
└── bstr feature "unicode" (*)
165+
```
166+
167+
This snippet of output shows that the project `foo` depends on `bar` with the "default" feature.
168+
Then, `bar` depends on `bstr` as a build-dependency with the "default" feature
169+
We can further see that `bstr`'s "default" feature enables "unicode" (among other features).
170+
171+
[`cargo tree`]: ../../cargo/commands/cargo-tree.html

0 commit comments

Comments
 (0)