デフォルトの Cargo のフィーチャリゾルバ

概要

  • edition = "2021" では Cargo.tomlresolver = "2" が設定されているとみなされます。

詳細

Rust 1.51.0 から、Cargo には新しいフィーチャリゾルバがオプトインできるようになっています。 これは、Cargo.tomlresolver = "2" と書くことで有効化できます。

Rust 2021 から、これがデフォルトになりました。 つまり、 Cargo.tomledition = "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"

我々のパッケージでは、今までは bstrwords_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 などの) ターゲットをビルドするときに必要でない限り、フィーチャは有効化されません。

実際の例としては、dieseldiesel_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" (*)

この出力例からは、foobar に "default" フィーチャ付きで依存していることがわかり、 bar はビルド時の依存として bstr に "default" フィーチャ付きで依存していることもわかります。 さらに、bstr の "default" フィーチャによって "unicode" フィーチャ(と、他のフィーチャも)が有効になっていることもわかります。