How to have an optional dependency enable another optional dependency rust - rust

I am creating a library in rust and I have different features which the user can enable via optional dependencies. I want it so if the dependency diesel is enabled than diesel-derive-enum should also be enabled.
[dependencies]
diesel = {version = "1.4.6", optional = true, features = ["postgres", "chrono"]}
diesel-derive-enum = {version = "1.1.1", optional = true, features = ["postgres"]}

As of Rust 1.60.0, with the stabilization of namespaced dependencies, you can now do this without having to add a differently named feature, using dep: features.
[dependencies]
diesel = {version = "1.4.6", optional = true, features = ["postgres", "chrono"]}
diesel-derive-enum = {version = "1.1.1", optional = true, features = ["postgres"]}
[features]
diesel = ["dep:diesel", "dep:diesel-derive-enum"]
Now you can simply pass --features diesel to cargo to enable both the diesel and diesel-derive-enum optional dependencies.
See the Cargo Book's section on Optional Features for more details.

You can use cargo features to enable multiple optional dependencies.
Here is an example:
[dependencies]
cli-color = { version = "0.1.20", optional = true }
clap= { version = "0.2.3", optional = true }
[features]
cli = ["cli-color", "clap"]
Also a real world example coming from tokio.
https://github.com/tokio-rs/tokio/blob/master/tokio/Cargo.toml
For more details see official rust docs:
https://doc.rust-lang.org/cargo/reference/features.html#the-features-section

Related

How to publish a crate with optional dependency?

I have some trouble about publishing crates with optional dependencies.
First, I execute cargo publish, but I don't see any optional dependencies compiled.
Then, I run cargo publish --all-features. The dependencies were compiled, but I don't see any modules with optional dependency in the documentation generated by crates.io.
What is the correct way to publish a crate with an optional dependency, which does not a default feature setting in Cargo.toml?
cargo publish is the correct way to do it.
If you want that docs.rs (not crates.io) builds your documentation with some features enabled, use the package.metadata.docs.rs section in your Cargo.toml. I use the Cargo.toml from the petgraph crate as an example (stripped):
[package]
name = "petgraph"
version = "0.6.0"
description = "Graph data structure library. Provides graph types and graph algorithms."
edition = "2018"
[dependencies]
fixedbitset = { version = "0.4.0", default-features = false }
indexmap = { version = "1.6.2" }
quickcheck = { optional = true, version = "0.8", default-features = false }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
[...]
[features]
default = ["graphmap", "stable_graph", "matrix_graph"]
graphmap = []
stable_graph = []
matrix_graph = []
serde-1 = ["serde", "serde_derive"]
[package.metadata.docs.rs]
features = ["serde-1", "quickcheck"]

How to include dependency based on optional dependency?

In Rust Cargo, how can I include optional features of dependencies, depending on the inclusion of another dependency?
Specifically, I would like to offer a "serde" feature, which enables serde support. What I can do is [1]
[dependencies]
serde = { version = "1.0", optional = true }
Rust exports this dependency as a feature automatically (as I understand it).
However, I use other dependencies, which offer a "serde" feature, too:
[dependencies]
otherpackage = { version = "1.0", features = ["serde"] }
serde = { version = "1.0", optional = true }
Now, the serde feature of "otherpackage" should only be included if the "serde" feature is activated for this package. But cargo does not let me do
[features]
serde = [ "serde", "otherpackage/serde" ]
[dependencies]
otherpackage = { version = "1.0" }
serde = { version = "1.0", optional = true }
I understand this is due to a name clash between the "serde" feature and the "serde" dependency, which I think is also exported as a feature. So, in essence there are now two "serde" features, which obviously does not work.
How can I solve this with still offering a "serde" feature and without renaming the "serde" package? Ideally, I would just like to disable the automatic exporting of the serde dependency as a feature or be able to specify a feature dependency of the "serde" package.
How can I solve this with still offering a "serde" feature and without renaming the "serde" package?
Since Rust 1.60, “namespaced features” allow this, as documented in the Cargo documentation on optional dependencies. When referring to a crate from within your [features] table, prefix it with dep:, like this:
[features]
serde = [ "dep:serde", "otherpackage/serde" ]
[dependencies]
otherpackage = { version = "1.0" }
serde = { version = "1.0", optional = true }
As soon as you use dep: for an optional dependency, it is no longer treated as implicitly creating a feature, so this also allows giving an entirely different name to the feature than to any dependencies it enables.
The following solution is obsolete and included for historical reference.
Prior to namespaced features, this had to be done by renaming the “serde” dependency. However, this doesn't need to disrupt anything else; as described in “Advanced Cargo [features] Usage”, you can rename it and rename it back. Set up your Cargo.toml like this:
[features]
serde = [ "serde_cr", "otherpackage/serde" ]
[dependencies]
otherpackage = { version = "1.0" }
serde_cr = { package = "serde", version = "1.0", optional = true }
This will make the crate name serde_cr visible to your code. Then rename it back using an explicit extern crate in your lib.rs:
extern crate serde_cr as serde;
However, this has the disadvantage of causing your package to have a feature named serde_cr.

Can I set Cargo project's default features depending on the platform?

Is it possible to make the list of default features platform dependent in your Cargo.toml? I'd like to use features to select platform-dependent dependencies.
I'd imagine something like this:
[features]
# on Unix
default = ["feature-a"]
# not on Unix
default = ["feature-b"]
feature-a = ["dep-a"]
feature-b = ["dep-b"]
[dependencies]
dep-a = { version = "*", optional = true }
dep-b = { version = "*", optional = true }
I've tried:
Using [target.'cfg(unix)'.features] does not work, it is ignored:
[target.'cfg(unix)'.features]
default = ["feature-a"]
# -- snip --
Using a build.rs script to enable features based on cfg conditions only partially works. Dependency resolution is done before running build.rs, so this won't import optional dependencies for features enabled in it. This example won't import dep-a:
fn main() {
#[cfg(unix)]
println!("cargo:rustc-cfg=feature=\"feature-a\"");
// -- snip --
}
Can this be achieved within Rust itself, without external scripts?
You can specify different features under each cfg. There is no support of cfg for root section features. So you can reach your goal but in a different way.
You can have different dependencies with different set of features.
For example:
[target.'cfg( target_os = "android" )'.dependencies]
some_dep = { version = "0.3.0", features = [ "feature1" ] }
[target.'cfg( target_os = "ios" )'.dependencies]
some_dep = { version = "0.3.0", features = [ "feature2" ] }
Official documentation.

Can cargo.toml contain custom properties

I've recently started with Rust and coming from a Node background I'm curious if the cargo.toml file for a project can be extended to include custom properties.
For example, in Node, some packages let you put configuration options in the package.json file (like babel):
{
"name": "my-package",
"version": "1.0.0",
"babel": {
"presets": [ ... ],
"plugins": [ ... ],
}
}
I've looked through the manifest docs but I can't seem to find anything about custom properties.
For example, what if I had a CLI tool and it has a configurable option. Do I have to have the user make a config file just for this option or could I include in the cargo.toml like so:
[package]
name = "my_project"
version = "0.1.0"
edition = "2018"
# custom property
store: ["one", "two", "three"]
[dependencies]
Also, if this is a "sure, as long as you have name and version as required", is it something that should be avoided?
The Rust equivalent would be Cargo features. You can define features for a crate like this in the crate's Cargo.toml:
[package]
name = "crate_with_features"
version = "1.0.0"
edition = "2018"
[features]
# default set of features
default = ["has_foo", "has_bar"]
# can use these for conditional compilation
has_foo = []
has_bar = []
And then if you had a lib.rs like this:
[cfg(feature = "has_foo")]
pub fn foo() {
println!("foo");
}
[cfg(feature = "has_bar")]
pub fn bar() {
println!("bar");
}
People would be able to select just which features of your crate they want. For example:
[package]
name = "other_project"
[dependencies]
crate_with_features = "1.0.0"
In the default case, crate_with_features would come with both foo and bar functions since the default feature requires both of them, but people could choose to only use one or the other by specifying:
[package]
name = "other_project"
[dependencies.crate_with_features]
version = "1.0.0"
# don't include default feature set
default-features = false
# cherry pick individual features
features = ["has_foo"]
In the above example crate_with_features would only be compiled with the foo function for that project.
The Rust Reference has a page on conditional compilation if you'd like to learn more about that specifically.

How to stop recompiling an extern crate in Rust

I have a local dependency on some SDK. I make use of
extern crate local_sdk;
use local_sdk::foo;
to use the local_sdk in my implementation.
I am making use of cargo to build. While building, the logs print the following at some stage -
Compiling local_sdk v0.1.0 (file:///project/project-core/sdk/rust)
This happens even though I have already compiled the local_sdk earlier.
How do I prevent recompiling the local_sdk ? It consumes some significant time.
Contents of the my Cargo.toml:
[package]
name = "service"
version = "0.1.0"
authors = ["Rajeev"]
[dependencies]
local_sdk = { path = "../../sdk/rust" }
The local_sdk has the following Cargo.toml:
[package]
name = "local_sdk"
version = "0.1.0"
authors = ["Rajeev"]
[dependencies]
hex = "0.3"
protobuf="2.0"
rand = "0.4.2"
zmq = { git = "https://github.com/erickt/rust-zmq", branch = "release/v0.8" }
uuid = { version = "0.5", features = ["v4"] }
log = "0.3"
libc = "0.2"
ctrlc = { version = "3.0", features = ["termination"] }
[dev-dependencies]
env_logger = "0.3"
[build-dependencies]
cc = "1.0"
glob = "0.2"
Assuming you're using it, there was an issue with RLS that was causing unnecessary rebuilds.
This issue has been fixed, to get the latest version of RLS, use
$ rustup update

Resources