Reputation: 2133
When compiling a Rust workspace composed of multiple packages, is there a way to make cargo
error when the succesfull compilation of a package foo
depends on features that are not enabled by foo
nor its dependencies?
Let me give a concrete example. With the workspace described below cargo build
succeeds, but cargo build -p foo
gives a compilation error because a tokio
feature is missing in foo/Cargo.toml
:
error[E0432]: unresolved import `tokio::io::AsyncWriteExt`
--> foo/src/lib.rs:1:5
|
1 | use tokio::io::AsyncWriteExt;
| ^^^^^^^^^^^-------------
| | |
| | help: a similar name exists in the module: `AsyncWrite`
| no `AsyncWriteExt` in `io`
For more information about this error, try `rustc --explain E0432`.
error: could not compile `foo` due to previous error
This is bad! cargo build
happily accepts usages of indirect dependencies. That is, changing code that is not in foo
nor in its dependencies (i.e. removing the io-util
feature from bar/Cargo.toml
) can introduce compilation errors in foo
. I would like to detect and prevent this without having to try each time to compile each single package individually.
Structure of the workspace:
.
├── foo
│ ├── src
│ │ └── lib.rs
│ └── Cargo.toml
├── bar
│ ├── src
│ │ └── main.rs
│ └── Cargo.toml
└── Cargo.toml
./foo/src/lib.rs
:
use tokio::io::AsyncWriteExt;
pub fn dummy(_: impl AsyncWriteExt) {
unimplemented!()
}
./foo/Cargo.toml
:
[package]
name = "foo"
version = "0.1.0"
edition = "2021"
[dependencies]
"tokio" = "1.21.2"
./bar/src/main.rs
:
fn main() {
println!("Hello, world!");
}
./bar/Cargo.toml
:
[package]
name = "bar"
version = "0.1.0"
edition = "2021"
[dependencies]
"tokio" = { version = "1.21.2", features = ["io-util"] }
"foo" = { path = "../foo" }
./Cargo.toml
:
[workspace]
members = [
"foo",
"bar",
]
Upvotes: 6
Views: 592
Reputation: 881
Unfortunately, cargo build --workspace
isn't sufficient for this, as it builds all package members at once, performing feature unification.
To build every package in a workspace individually, you can use the cargo-hack
subcommand.
To install the subcommand, run:
cargo install cargo-hack
Once it's installed, you can run the following command to build all workspace packages individually:
cargo hack build --workspace
The output is as you expect:
error[E0432]: unresolved import `tokio::io::AsyncWriteExt`
--> foo/src/lib.rs:1:5
|
1 | use tokio::io::AsyncWriteExt;
| ^^^^^^^^^^^-------------
| | |
| | help: a similar name exists in the module: `AsyncWrite`
| no `AsyncWriteExt` in `io`
For more information about this error, try `rustc --explain E0432`.
error: could not compile `foo` due to previous error
This is the equivalent of building each package individually, but can be done in a single command. See the README entry for the --workspace
flag for more details.
Upvotes: 1