Obligatory read on this topic:
Basically, the [dependencies]
of a package apply to all crates within that package. In this instance, it means it applies not only to your (main) [[bin]]
ary crate, but also to your [lib]
crate, that is, the library on which dependents would depend.
So yes, if binary-specific dependencies, such as clap
or other CLI helpers, are present under the [dependencies]
, and are not marked optional = true
, then it does mean that dependents (on your [lib]
rary) will also be transitively depending on those libraries.
That being said, if they end up never used, then they will very probably not be present in the final linked binary, since they'll probably be elided at link-time.
But since the Cargo pipeline will not know whether they are used or not, it does mean that dependents will have to fetch-at-least-once, and compile, each of these unnecessary dependencies.
The simple solution
Is to make the binary/CLI-specific dependencies optional, behind, e.g., some "cli"
Cargo feature:
[dependencies]
# ...
# Before:
# clap = "x.y.z"
# Now:
clap.version = "x.y.z"
clap.optional = true
# etc.
# ...
[features]
cli = [ # <- added!
"dep:clap",
# etc.
]
From there, to try and get nicer diagnostics when your [[bin]]
ary crate is being compiled, you can add "cli"
to the set of required-features
for your [[bin]]
:
[[bin]]
name = "your-package"
path = "src/main.rs"
required-features = ["cli"]
This, alas, does not mean that Cargo is to enable that feature when trying to build the --bin
crate, but rather, that Cargo will just complain when trying to build such a crate without passing --features cli
.
- If directly
cargo <check|build|run>
ning, this complaint will be a nice hard error
- But if a user is
cargo install
ing it, then it will just be a warning, and a silently non-installed binary, which is quite awful UX (this is what the blog post above complains about).
My own take on it
What the blog post fails to suggest, and which I would then personally do instead, would be to, instead of using required-features
in the Cargo.toml
, to enforce it within the Rust code, ourselves (at least until Cargo decides to properly do it by itself).
For instance, using compile_error!()
within the src/main.rs
:
The optimal, albeit cumbersome, solution
which is suggested in that blog post, is simply to forgo the single-package-multiple-crates utopia, and to instead embrace the existence of two Cargo packages, one to be the canonical [lib]
rary crate, and the other one, to be the canonical [[bin]]
ary crate, with a now necessary explicit dependency on the former by the latter.