LoginSignup
0
0

More than 3 years have passed since last update.

Cargoの[dev|build]-dependenciesに関するバグ

Posted at

TL;DR

  • 現在のstableチャンネルのCargo1では、ルートクレート2の依存パッケージを決定する際にdev-dependenciesbuild-dependenciesも考慮してしまう。
  • nightlyのCargoでは修正されていて、cargo +nightly -Z features=dev_dep [Command]cargo +nightly -Z features=host_dep [Command]とすることで回避できる。3 4

バグの紹介

 Cargo.tomlにはdev-dependenciesという項目を追加でき、testsexamplesなどをビルドする際に必要な依存クレートを記すために使われる。この目的から、この項目に記載されているものはライブラリや実行バイナリ本体のビルドには不必要なものである事が容易に分かる(そもそも本体のビルドに必要であればdependenciesに記すだけで十分である)。
 しかし、現在のstableチャンネルから得られるCargoはプロダクト本体をビルドする際にこのdev-dependenciesのビルドに記されている依存クレートも考慮に入れて依存関係の解決をしてしまう。このバグはbuild.rsで必要になる依存クレートを記載するためのbuild-dependenciesについても同様に発生する。

問題が起きるケース

このバグは致命的なエラーを引き起こすことは少ないが、特定の条件ではライブラリのバグを見逃してしまう。例えば開発しているライブラリがnum-traitsに依存していて以下のような項目を含むCargo.tomlであるとしよう。

Cargo.toml
[dependencies]
num-traits = { version = "0.2.12", default-features = false }

[dev-dependencies]
num-traits = { version = "0.2.12", features = ["std"] }

num-traitsはデフォルトでstdフィーチャーが有効になっているが、ここではdefault-features = falseとすることによって無効化している。一方、dev-dependenciesではstdフィーチャーが必要な場合を想定している。

ここで、num-traitsFloatトレイトをインポートしてみる。

src/lib.rs
#[allow(unused_imports)]
use num_traits::float::Float;

実はFloatトレイトはnum-traitsstdフィーチャーが有効化されている時のみ定義されているので、このコードはビルドに失敗して然るべきである。しかしCargoのバグによりこのライブラリをビルドする際にはdev-dependenciesが考慮されるので、num-traitsstdフィーチャーが有効化された状態でビルドされ、このライブラリはビルドが成功してしまう。しかし、このライブラリに依存するクレートをビルドしようとした時には問題が起こる。

このライブラリの名前をdev_dep_bugとして、このクレートと同じディレクトリに、dev_dep_bugに依存するバイナリクレートを新たに作ろう。

Cargo.toml
[dependencies]
dev_dep_bug = { version = "*", path = "../dev_dep_bug"}

依存クレートをビルドする際は、依存クレートのdev-dependenciesは考慮されないのでnum-traitsstdフィーチャーが無効化された状態でビルドする。これによってdev_dep_bugをビルドする際にFloatトレイトが見つからず、以下のようなエラーが吐き出される。

$ cargo check
    Updating crates.io index
   Compiling autocfg v1.0.1
   Compiling num-traits v0.2.12
    Checking dev_dep_bug v0.1.0 (/path/to/dev_dep_bug)
error[E0432]: unresolved import `num_traits::float::Float`
 --> /path/to/dev_dep_bug/src/lib.rs:2:5
  |
2 | use num_traits::float::Float;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ no `Float` in `float`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
error: could not compile `dev_dep_bug`.

To learn more, run the command again with --verbose.

この問題はライブラリ使用者が手の付けられない範囲に原因が潜んでおり、IssueやPRを投げる他に対策方法が無いことにある(forkしたり手元にライブラリのコードを持ってきて修正したものを使用するという手があるが、そこまでやるならPRを投げてContributeした方が良いと思う)。
しかもライブラリ開発者は手元であれCIであれ、単純にcargo checkcargo buildをするだけでは気づき得ないという点が凶悪である。

解決方法

このバグはnightlyチャンネルでは既に修正されており、-Zフラグを使ってfeatures=dev_dep3features=host_dep4を指定することによってそれぞれdev-dependenciesbuild-dependenciesのバグを回避でき、ライブラリがこれに起因する依存関係のバグを仕込んでいないか確認が取れる。

$ cargo +nightly -Zfeatures=dev_dep check # or build
$ cargo +nightly -Zfeatures=host_dep check # or build

最後に先程の例をnightlyのCargoでビルドしてみると、以下の通り正しくビルドエラーになる。

$ cargo +nightly -Zfeatures=dev_dep check
    Checking dev_dep_bug v0.1.0 (/path/to/dev_dep_bug)
error[E0432]: unresolved import `num_traits::float::Float`
 --> src/lib.rs:2:5
  |
2 | use num_traits::float::Float;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^ no `Float` in `float`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0432`.
error: could not compile `dev_dep_bug`.

To learn more, run the command again with --verbose.

  1. cargo 1.46.0 (149022b1d 2020-07-17) 

  2. cargoコマンドを呼ぶ時にいるディレクトリのプロジェクト。正式な呼称が分からなかったのでこのように呼んでいる。 

  3. https://github.com/rust-lang/cargo/issues/7915 

  4. https://github.com/rust-lang/cargo/issues/7916 

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0