At the moment, dependencies in Cargo.toml
are coarse-grained.
In theory a crate can contain:
- One library.
- Multiple binaries.
- Multiple example binaries.
- One unit-test binaries.
- Multiple doc-test binaries.
- Multiple integration test binaries.
- Multiple benchmark binaries.
- One build-script binary.
And it's quite useful to have a single crate contain all those, as it means that one's fingertips, one gets to run cargo check
, cargo clippy --all-targets
, cargo test
, cargo bench
, etc...
Unfortunately, dependency-wise, it's a complete nightmare.
There's only 3 categories of dependencies in cargo:
[dependencies]
means that if one binary includes heavy-weight dependencies, like, saytokio
, then any crate wishing to use the library will also depend on (and bundle)tokio
now. Tough luck.[dev-dependencies]
means that if there's a single benchmark depending oncriterion
, then all unit-test, doc-test, and integration test binaries will also depend (and bundle)criterion
.[build-dependencies]
is fairly nicely insulated at least, though with multiple build scripts being worked on... a single build script depending on a heavyweight dependency will drag down all other build scripts.
The current work-around is to break down the crates. Binaries must be their own crates. Examples and integration tests are better off in their own crates. But then convenience is lost. Doubly so as running cargo in a folder which is not a crate does NOT limit cargo to running only on the crates contained in this folder, but instead lead to cargo attempting to run on the entire workspace.
Is there any work towards making the dependencies more fine-grained?
Ideally, one would be capable of specifying the dependencies for each compilation target/artifact independently, something like:
[dependencies.lib]
[dependencies.bin.my-binary-1]
[dev-dependencies.test]
[dev-dependencies.doctest]
[dev-dependencies.examples.my-example-1]
[dev-dependencies.tests.my-integration-test-1]
[build-dependencies.my-script-1]
Which would be backward compatible with using a plain unqualified [dependencies]
or [dev-dependencies]
or [build-dependencies]
as today.
Note: in the context of multiple build scripts, it would notably allow starting to run fast-to-compile build scripts while the heavyweight dependencies of other build scripts are still being compiled.
It's not that the state of things today is unworkable. It's just... sad. One has to choose between convenience/ergonomics and compile-time performance/binary size1.
1 Especially so as apparently, per Kobolz discovery, debug information are not tree-shaked, so that including a heavyweight dependency in the binary results in said binary containing the DI of this dependency even if not its code.