デフォルトの Cargo のフィーチャリゾルバ
概要
edition = "2021"
ではCargo.toml
でresolver = "2"
が設定されているとみなされます。
詳細
Rust 1.51.0 から、Cargo には新しいフィーチャリゾルバがオプトインできるようになっています。
これは、Cargo.toml
で resolver = "2"
と書くことで有効化できます。
Rust 2021 から、これがデフォルトになりました。
つまり、 Cargo.toml
に edition = "2021"
と書けば、暗黙に resolver = "2"
も設定されているとみなされます。
このリゾルバはワークスペース全体に設定され、依存先では無視されます。
また、この設定はワークスペースの最上位のパッケージでしか効きません。
仮想ワークスペースにおいて新しいリゾルバをオプトインしたい場合は、
以前と同様に resolver
フィールドを明示的に設定する必要があります。
新しいフィーチャリゾルバは、クレートへの依存に異なるフィーチャが設定されていてもそれらをマージしないようになりました。 詳細は the announcement of Rust 1.51 に記載されています。
移行
新しいリゾルバに適合させるための自動化された移行ツールはありません。 ほとんどのプロジェクトでは、更新後に必要な変更はあっても微々たるものでしょう。
cargo fix --edition
でのアップデート時に、Cargo は新しいリゾルバで依存先のフィーチャに変更があるかどうかを表示します。
たとえば、このように表示されます:
note: Switching to Edition 2021 will enable the use of the version 2 feature resolver in Cargo. This may cause some dependencies to be built with fewer features enabled than previously. More information about the resolver changes may be found at https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html
When building the following dependencies, the given features will no longer be used:(訳) 2021 エディションに切り替えると、Cargoのフィーチャリゾルバがバージョン 2 に切り替わります。 切り替え後、いくつかの依存先では有効化されるフィーチャが減少することがあります。 リゾルバの変更点については、 https://doc.rust-lang.org/nightly/edition-guide/rust-2021/default-cargo-resolver.html もご覧ください。
以下の依存先をビルドするときに、以下のフィーチャが使われなくなります:bstr v0.2.16: default, lazy_static, regex-automata, unicode libz-sys v1.1.3 (as host dependency): libc
これにより、記載されたフィーチャがその依存先で使われずにビルドされるようになることがわかります。
ビルドの失敗
状況によっては、変更後にプロジェクトが正しくビルドされなくなることもあります。 あるパッケージの依存関係が、別のパッケージにおいて特定のフィーチャが有効されることを前提にしている場合、そのフィーチャが使われなくなることでコンパイルに失敗するかもしれません。
たとえば、我々のパッケージにこんな依存関係があったとしましょう:
# Cargo.toml
[dependencies]
bstr = { version = "0.2.16", default-features = false }
# ...
そして依存関係の中にはこんなパッケージもあるとしましょう:
# Another package's Cargo.toml
# 別のパッケージの Cargo.toml
[build-dependencies]
bstr = "0.2.16"
我々のパッケージでは、今までは bstr
の words_with_breaks
関数を使用していたとします。この関数は(本来) bstr
の "unicode" フィーチャを有効化しないと使えないものです。
歴史的事情から、今まではこれでもうまくいきました。というのも、Cargo は2つのパッケージで使われている bstr
のフィーチャを共通化していたからです。
しかしながら、Rust 2021 へのアップデート後、 bstr
は1回目(ビルド依存関係として)はデフォルトのフィーチャで、2回目(我々のパッケージの通常の依存先として)はフィーチャなしで、合計2回ビルドされます。
今や bstr
は "unicode" フィーチャなしでビルドされるので、 words_with_breaks
メソッドは存在せず、メソッドがないというエラーが発生してビルドは失敗します。
ここでの解決策は、依存関係の宣言に我々が実際に使っているフィーチャを書くようにすることです。
[dependencies]
bstr = { version = "0.2.16", default-features = false, features = ["unicode"] }
ときには、あなたが直接いじることのできないサードパーティな依存先で問題が発生することもあります。
その場合は、問題が起こっている依存関係について、正しくフィーチャを指定するように、そのプロジェクトにパッチを送るのもよいでしょう。
あるいは、自身の Cargo.toml
に記載する依存関係にフィーチャを追加することもできます。
新しいリゾルバには以下のような併合ルールがあり、その下でフィーチャは併合されます。すなわち、
- 現在ビルドされていないターゲットに対するプラットフォーム特有の依存関係で有効化されているフィーチャは、無視されます。
- build-dependencies と proc-macro では、通常の依存関係とは独立したフィーチャが使用されます。
- dev-dependencies では、(tests や examples などの) ターゲットをビルドするときに必要でない限り、フィーチャは有効化されません。
実際の例としては、diesel
と diesel_migrations
を使用する場合が挙げられます。
これらのパッケージはデータベースへのサポートを提供しますが、データベースはフィーチャを用いて選択されます。たとえば、こんな感じです:
[dependencies]
diesel = { version = "1.4.7", features = ["postgres"] }
diesel_migrations = "1.4.0"
ここで問題なのは、 diesel_migrations
は内部に diesel
に依存する手続き的マクロをもちます。
この手続き的マクロは、自身が使用する diesel
で有効化されているフィーチャが、依存関係木の他の場所で有効化されているものと同じであると仮定します。
ところが、新しいリゾルバが使用されると、2つの diesel
が使用され、そのうち手続き的マクロ用のものは "postgres" フィーチャなしでビルドされるために、ビルドに失敗します。
ここでの解決策は、diesel
をビルド時の依存として追加し、そこに必要なフィーチャを指定することです。例えば以下のようになります。
[build-dependencies]
diesel = { version = "1.4.7", features = ["postgres"] }
これにより、 Cargo はホスト依存関係(proc-macro と build-dependencies)のフィーチャとして "postgres" を追加します。
訳注:ホスト依存関係とは、コンパイラホスト(コンパイラを実行しているプラットフォーム)向けにビルド・実行される依存を指し、proc-macro クレートや build-dependencies 配下の依存クレートが該当します。 一方、通常の依存関係はコンパイルターゲットのプラットフォーム向けにビルドされます。
これで、 diesel_migrations
の手続き的マクロは "postgres" フィーチャが有効化された状態で走り、正しくビルドされます。
(現在開発中の) diesel
のリリース 2.0 では、このような仮定なしに動くよう再設計されているため、このような問題は発生しません。
フィーチャを探索する
cargo tree
コマンドには、新しいリゾルバへの移行を補助する、素晴らしい新機能が含まれています。
cargo tree
を使えば、依存関係木を探索して、どのフィーチャが有効化されているか、そしてなによりなぜそれが有効化されているのかが分かります。
例えば、--duplicates
(短縮形: -d
) フラグを使用すると、同じパッケージが複数回ビルドされている場所がわかります。
さきほどの bstr
を例に取れば、このような表示になるでしょう:
> cargo tree -d
bstr v0.2.16
└── foo v0.1.0 (/MyProjects/foo)
bstr v0.2.16
[build-dependencies]
└── bar v0.1.0
└── foo v0.1.0 (/MyProjects/foo)
この出力から、bstr
が複数回ビルドされていることと、どの依存関係をたどると双方が現れるかが分かります。
-f
フラグを使えば、それぞれのパッケージがどのフィーチャを使用しているかがわかります。こんな感じです:
cargo tree -f '{p} {f}'
こうすると、Cargo は出力の「フォーマット」を変更して、パッケージと有効化されているフィーチャの双方を表示するようになります。
さらに、-e
フラグを使用してどの「辺」を表示してほしいか指定することもできます。
例えば、cargo tree -e features
とすれば、各依存関係の間に、各依存関係がどのフィーチャを追加しているのかが表示されます。
-i
フラグを使って木を「反転」させると、このオプションはより便利になります。
例えば、依存関係木があまりにも大きくて、何が bstr
に依存してるのかよくわからなくても、次のコマンドを実行すればいいです:
> cargo tree -e features -i bstr
bstr v0.2.16
├── bstr feature "default"
│ [build-dependencies]
│ └── bar v0.1.0
│ └── bar feature "default"
│ └── foo v0.1.0 (/MyProjects/foo)
├── bstr feature "lazy_static"
│ └── bstr feature "unicode"
│ └── bstr feature "default" (*)
├── bstr feature "regex-automata"
│ └── bstr feature "unicode" (*)
├── bstr feature "std"
│ └── bstr feature "default" (*)
└── bstr feature "unicode" (*)
この出力例からは、foo
が bar
に "default" フィーチャ付きで依存していることがわかり、
bar
はビルド時の依存として bstr
に "default" フィーチャ付きで依存していることもわかります。
さらに、bstr
の "default" フィーチャによって "unicode" フィーチャ(と、他のフィーチャも)が有効になっていることもわかります。